C# - 使用缓存进行延迟加载

时间:2014-09-29 12:37:49

标签: c# caching lazy-loading lazy-evaluation

我有一个存储库装饰器。该装饰器负责装饰存储库的缓存。在我的这个装饰器的大多数函数中,我只返回缓存的结果(如果存在)或者在装饰的存储库上调用方法,并将此结果存储在缓存中(如果尚未存在于此缓存中)。我这样做是安全的。

但是我想在一个方法中执行这个获取缓存锁定的例程,并使用lambda表达式调用它。

我获取缓存结果或加载缓存的方法:

private X CallCachedAndLocked<X>(string methodCacheKey, xxx methodToCallWithParameter)
{
    var cacheKey = GetCacheKey(methodCacheKey);
    X obj = (X)Cache.Get(cacheKey);
    if (obj == null)
   {
        lock (getLastDrawResult_lock)
        {
            if (obj == null)
            {
                obj = methodToCallWithParameter;
                if (obj != null)
                {
                    Cache.Add(cacheKey,
                        obj,
                        null,
                        NextExpiration,
                        System.Web.Caching.Cache.NoSlidingExpiration,
                        CacheItemPriority.AboveNormal, null);
                }
            }
        }
    }
}

电话示例:

public T GetDraw(int id)
{
    return CallCachedAndLocked<T>(() => _decoratedRepository.GetDraw(id));
}        

public IEnumerable<T> GetDraws(DateTime from)
{
    return CallCachedAndLocked<T>(() => _decoratedRepository.GetDraws(GetDraws));
}

3 个答案:

答案 0 :(得分:0)

我建议使用.Net中的Lazy类,它看起来像是您需要的匹配项:

var lazyCall = new Lazy<T>(() => new T());

并且在访问值

lazyCall.Value // launches evaluation of the lambda expression


您可以将任何lambda设置为Lazy的评估代码,因此只要您在存取访问器的范围内,就可以使用它们来运行初始化代码:

var c = MyCache.Get[key]();
if (c == null)
{
    c = methodToCallWithParameter(key);
    MyCache.Add(key, c);
}
return c;

或多或少等同于:

c = new Lazy<cType>(() => methodToCallWithParameter(key));
return c;

然后在调用代码中使用c.Value

答案 1 :(得分:0)

我终于找到了一个带有反射的解决方案。没有它,我没有看到解决方案: public static DateTime GetCachedMethod(int nbMonth, NonDecoratedClass repo) { var methodCacheKey = "Test"; DateTime obj = new DateTime();

        if (!cache.ContainsKey(methodCacheKey))
        {
            lock (zeLock)
            {
                if (!cache.ContainsKey(methodCacheKey))
                {
                    obj = repo.GetDateTimeAndMonth(nbMonth);
                    if (obj != null)
                    {
                        cache.Add(methodCacheKey, obj);
                    }
                }
            }
        }
        else
        {
            obj = (DateTime)cache[methodCacheKey];

        }
        return obj;
    }

if (!cache.ContainsKey(methodCacheKey)) { lock (zeLock) { if (!cache.ContainsKey(methodCacheKey)) { obj = repo.GetDateTimeAndMonth(nbMonth); if (obj != null) { cache.Add(methodCacheKey, obj); } } } } else { obj = (DateTime)cache[methodCacheKey]; } return obj; }

答案 2 :(得分:-1)

您可以使用以下行的扩展方法轻松完成此操作:

private static readonly _lock = new object();

public static void Lock(Action action)
{
    // Lock.
    lock (_lock) action();
}

public static T Lock<T>(Func<T> func)
{
    // Lock.
    lock (_lock) return func();
}

但是,你真的不应该这样做;您正在为所有内容共享相同的锁定,这只会导致争用。

您希望锁定尽可能精细,以便在锁定时不会占用其他等待的线程。在这里使用共享锁是精细的。