Dailycode.info

Short solution for short problems

EntityFramework: Adding IEnumerable to Cache.

My collegues were rewriting some code. This involved generic caching of default mapping objects. 

The mapper loops over a collection of database objects and maps them to business objects. Using seperate mapping tables in a seperate database, we had to map them in the loop. But we didn't want to get all the mappings from the database, instead cache them. 

When we looped over 870 objects and mapped them without the extra mappings, it finished in less then a second. When using the mapping to the database, it took about 2 minutes. Then we rewrote to use caching, but the first time the objects were filled in the cache, it still took 2 minutes. 

The code where the caching of the mappings was looked like this:

        private IEnumerable<TResult> GetMappings<TResult, TEntity>(bool refreshCache = false, Func<TEntity, TResult> mappingFunction = null)
            where TResult : StandardMapping, new()
            where TEntity : SQL.Mappings.StandardMapping, new()
        {
            var key = typeof(TResult).FullName.ToLower();
            if (refreshCache)
                CacheHelper.Remove(key);
            return CacheHelper.Get(key, () =>
            {
                var map = mappingFunction ?? GeneralMapper.FromSQL2BO<TResult>;
                return MasterDl.GetMappings<TEntity>().Select(m => map(m));//returning IEnumerable
            });
        }

The CacheHelper.Get would add the result if it was not cached yet. 

        public static T Get<T>(string key, Func<T> cacheMissCallback)
        {
            if (MemoryCache.Default.Contains(key))
            {
                return Get<T>(key);
            }
            else
            {
                var value = cacheMissCallback();
                Set(key, value);
                return value;
            }
        }

But still it took the same amount of time to load.

The problem was that the Enumerator was cached and not the result. So after I added a .ToList() that made sure the data was fetched from the DB , the delay was completely gone:

        private IEnumerable<TResult> GetMappings<TResult, TEntity>(bool refreshCache = false, Func<TEntity, TResult> mappingFunction = null)
            where TResult : StandardMapping, new()
            where TEntity : SQL.Mappings.StandardMapping, new()
        {
            var key = typeof(TResult).FullName.ToLower();
            if (refreshCache)
                CacheHelper.Remove(key);
            return CacheHelper.Get(key, () =>
            {
                var map = mappingFunction ?? GeneralMapper.FromSQL2BO<TResult>;
                return MasterDl.GetMappings<TEntity>().ToList().Select(m => map(m));//Tolist executes the query before returning an IEnumerable
            });
        }

Using an SQL profiler is a big help in these king of problems.

Here the result of the cached enumerator:


Here with the .ToList() added to resolve the Enumerator




blog comments powered by Disqus