GetCacheableRows and GetExpirationTimeout - correct usage for empty entity sets

Dec 10, 2014 at 2:05 PM
Hi,

It seems the CachePolicy's GetCacheableRows and GetExpirationTimeout methods are called even though the affected entity sets are empty. In those cases is the following the correct way to check and return the appropriate values?
        protected override void GetCacheableRows(System.Collections.ObjectModel.ReadOnlyCollection<EntitySetBase> affectedEntitySets, out int minCacheableRows, out int maxCacheableRows)
        {
            // are there any sets? If not then nothing to cache.
            if (!affectedEntitySets.Any())
            {
                minCacheableRows = -1;
                maxCacheableRows = -1;
                return;
            }
        protected override void GetExpirationTimeout(System.Collections.ObjectModel.ReadOnlyCollection<EntitySetBase> affectedEntitySets, out TimeSpan slidingExpiration, out DateTimeOffset absoluteExpiration)
        {
            // are there any sets? If not then nothing to cache.
            if (!affectedEntitySets.Any())
            {
                slidingExpiration = TimeSpan.Zero;
                absoluteExpiration = DateTimeOffset.MinValue;
                return;
            }
Kind regards
Sidharth
Dec 11, 2014 at 6:29 AM
Hi Sidharth,

Honestly I don't know - what is the scenario where affectedEntitySets is empty? Maybe these methods should not be invoked in this case or maybe it does not matter.

Thanks,
Pawel
Dec 17, 2014 at 11:31 AM
Hi Pawel,

I have finally been able to look into this and the reason was due to our unit tests sending a query that would never return anything. In this scenario, and when EF knows for sure that nothing can ever be returned e.g. finding all rows where column X is null but column X is a non-nullable column or filtering like myList.Contains(columnName) but myList is empty, then EF runs this special query:
SELECT
CAST(NULL AS varchar(1)) AS [C1],
CAST(NULL AS varchar(1)) AS [C2],
CAST(NULL AS varchar(1)) AS [C3],
CAST(NULL AS bit) AS [C4],
CAST(NULL AS varchar(1)) AS [C5],
CAST(NULL AS bit) AS [C6],
CAST(NULL AS varchar(1)) AS [C7],
CAST(NULL AS bit) AS [C8],
CAST(NULL AS bit) AS [C9]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
WHERE 1 = 0
This query never returns anything but for whatever reason EF will execute this SQL in the above scenarios. For reference: https://connect.microsoft.com/VisualStudio/feedback/details/696727/linq-to-entity-framework-submits-meaningless-query-to-sql-server-for-contains-empty-array

In the CachingPolicy.CanBeCached call what I can see is that the affectedEntitySets is empty and the SQL parameter is set to the above SQL. So what I've done in my Policy.CanBeCached is that I check if the affectedEntitySets is empty and if that is the case I return false. I've removed the code from the GetExpirationTimeout and GetCacheableRows methods now.

Kind regards
Sidharth
Dec 22, 2014 at 7:28 AM
Hi Sidharth,

I talked to Diego who manages EF development and he pointed me to this issue which was created from the connect bug you referred to in your post. Given that in the EF Cache I use the query and parameters (including values) as the key I think it should actually be fine to cache results for queries with empty affectedEntitySets permamently since they will never return non-empty resultsets.

Thanks,
Pawel