简体   繁体   中英

How to implement a generic cache manager in c#

I'm trying to implement a generic cache manager, however I'm not sure how to go about doing the locking.

I have the following so far, however if I have two cache entries with the same return types then I'm guessing the same lock object would be used!

public class CacheManager : ICacheManager
{
    static class TypeLock<T>
    {
        public static readonly object SyncLock = new object();
    }
    private readonly ICache _cache;
    public CacheManager(ICache cache)
    {
        if (cache == null)
            throw new ArgumentNullException("cache");

        _cache = cache;
    }

    public TResult AddCache<TResult>(string cacheKey, Func<TResult> acquire, int cacheDurationInMinutes) where TResult : class
    {
        return AddCache(cacheKey, null, acquire, cacheDurationInMinutes);
    }

    public TResult AddCache<TResult>(string cacheKey, CacheDependency dependency, Func<TResult> acquire, int cacheDurationInMinutes) where TResult : class
    {
        var entry = acquire.Invoke();
        if (entry != null)
        {
            if (dependency != null)
                _cache.InsertWithDependency(cacheKey, entry, dependency, DateTime.Now.AddMinutes(cacheDurationInMinutes));
            else
                _cache.Insert(cacheKey, entry, DateTime.Now.AddMinutes(cacheDurationInMinutes));
        }
        return entry;
    }

    public TResult GetOrAddCache<TResult>(string cacheKey, Func<TResult> acquire, int cacheDurationInMinutes) where TResult : class
    {
        return GetOrAddCache(cacheKey, null, acquire, cacheDurationInMinutes);
    }

    public TResult GetOrAddCache<TResult>(string cacheKey, CacheDependency dependency, Func<TResult> acquire, int cacheDurationInMinutes) where TResult : class
    {
        var entry = _cache.GetItem(cacheKey) as TResult;

        if (entry == null)
        {
            lock (TypeLock<TResult>.SyncLock)
            {
                entry = _cache.GetItem(cacheKey) as TResult;
                if (entry == null)
                {
                    entry = acquire.Invoke();
                    if (entry != null)
                    {
                        if (dependency != null)
                            _cache.InsertWithDependency(cacheKey, entry, dependency,
                                                        DateTime.Now.AddMinutes(cacheDurationInMinutes));
                        else
                            _cache.Insert(cacheKey, entry, DateTime.Now.AddMinutes(cacheDurationInMinutes));
                    }
                }
            }
        }

        return entry;
    }
}

Any help would be much appreciated!

You are correct, this would use the same lock for all cache entries of the same type. To keep you actual caching method (memory, file, database...) independent of this cache manager, I suggest storing an extra sync object for each cache entry, so your cache entries will look for example like Tuple<object, TResult> instead of just TResult , where the object will be a new object() for each entry and will be used for locking.

You should never lock in your caching code unless its really needed. Use Concurrent dictionary if you don't want to use any well known cache.

I'd also say you probably should use the System.Runtime.MemorCache instead.

Or you use a solution which does abstract this for you, like my CacheManager https://github.com/MichaCo/CacheManager ;)

I'm not sure you should use lock, alos, why not use MemoryCache instead of ICache interface? Isn't memory cache generic enough? Why did you force cache time to be in minute, you should pass a timespan to your cache methods, I would be more generic.

I implemented a version of a cache manager few month ago, but it was web oriented.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM