C# In-Memory Cache

Date: 2018-03-19
using System;
using System.Runtime.Caching;

namespace Default
{
    public class CacheHelper
    {
        private static readonly ObjectCache Cache = MemoryCache.Default;
//        private static readonly ObjectCache ObjectCache = new MemoryCache("WebApiCache");
        private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>();
        public static T Get<T>(string key)
        {
            return (T)Cache.Get(key);
        }

        public static void Add(string key, object value, int minutes)
        {
            var item = new CacheItem(key) {Value = value};
            var policy = new CacheItemPolicy {AbsoluteExpiration = DateTime.UtcNow.AddMinutes(minutes)};
            Cache.Add(item, policy);
        }
        public static T GetFromCache<T>(string key)
        {
            return (T)(ObjectCache.GetCacheItem(key)?.Value);
        }

        public static void AddToCacheNonExpiring(string key, object value)
        {
            var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.MaxValue };

            var cacheItem = new CacheItem(key, value);
            ObjectCache.Add(cacheItem, policy);
        }

        public static void AddToCacheAbsolute(string key, object value, DateTimeOffset expiresAt)
        {
            var policy = new CacheItemPolicy { AbsoluteExpiration = expiresAt };

            var cacheItem = new CacheItem(key, value);
            ObjectCache.Add(cacheItem, policy);
        }

        public static async Task<T> CachedItem<T>(string key, Func<Task<T>> generator, TimeSpan validTime)
        {
            using (new Locker(key))
            {
                var cacheValue = ObjectCache.GetCacheItem(key)?.Value;
                if (cacheValue != null)
                    return (T)cacheValue;
                T result = await generator().ConfigureAwait(false); // Configure await false ensures the function throws errors
                var cacheItem = new CacheItem(key, result);
                var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(validTime) };
                ObjectCache.Add(cacheItem, policy);
                return result;
            }
        }

        public static T CachedItem2<T>(string key, Func<T> generator, TimeSpan validTime)
        {
            try
            {
                lock (keyLocks.GetOrAdd(key, new object()))
                {
                    var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(validTime) };

                    var cacheValue = ObjectCache.GetCacheItem(key)?.Value;
                    if (cacheValue != null)
                        return (T)cacheValue;
                    // Configure await false ensures the function throws errors;
                    T result = generator();
                    if (result == null)
                        return default(T); // null can not be cached
                    var cacheItem = new CacheItem(key, result);
                    ObjectCache.Add(cacheItem, policy);
                    return result;
                }
            }
            finally
            {
                keyLocks.TryRemove(key, out object _);
            }
        }

        private class CachedData<T> : ICachedItem<T>
        {
            public DateTime GeneratedAt { get; set; }
            public T Result { get; set; }
        }


        public ICachedItem<T> CachedItem<T>(string key, Func<T> generator, TimeSpan validTime, TimeSpan regenerateAfter)
        {
            var cachedData = (CachedData<T>)ObjectCache.GetCacheItem(key)?.Value;           
            if (cachedData != null) {
                if (cachedData.GeneratedAt < DateTime.Now.Subtract(regenerateAfter)) { 
                    Task.Run(() => { 
                        return GenerateNew(key, generator, validTime);    
                    });
                }
                return cachedData;
            }
            return GenerateNew(key, generator, validTime);    
        }

        public ICachedItem<T> GenerateNew<T>(string key, Func<T> generator, TimeSpan validTime) { 
            var result = generator();
            if (result == null)
                return default; // null can not be cached

            var data = new CachedData<T> { 
                GeneratedAt = DateTime.Now,
                Result = result
            };
            var cacheItem = new CacheItem(key, data);
            var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(validTime)};
            ObjectCache.Set(cacheItem, policy);
            return data;
        }
    }
}

8940cookie-checkC# In-Memory Cache