2014-09-29 115 views
0

我有一个存储库修饰器。这个装饰器负责装饰存储库的缓存。在这个装饰器的大部分函数中,我只是返回缓存的结果(如果存在),或者调用装饰存储库上的方法,并将此结果存储在缓存中(如果尚未存在于此缓存中)。我做那个安全。C# - 缓存加载缓存

但我想做这个例程获取缓存锁,...在一个单一的方法,并调用它与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)); 
} 
+0

你想传递的关键和lambda,或lambda只? – 2014-09-29 12:42:19

+1

我不认为你需要这里的双重检查锁。 obj是一个局部变量,它在堆栈中,不会被共享等等。不是吗? – ziya 2014-09-29 12:55:36

+0

我不完全明白这个问题。底层存储库调用是否需要同步发生? – ziya 2014-09-29 12:58:53

回答

0

我建议使用从.NET中Lazy类,它看起来像一个比赛对于你所需要的:

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

和访问值

lazyCall.Value // launches evaluation of the lambda expression


您可以设置任何lambda作为懒惰的评估代码,所以只要你是在一个范围在您存取存在的时候,你可以用它们来运行初始化代码:

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

+0

我不认为它会做的伎俩在这里有两个原因: - 我必须传递一些参数并调用我的存储库上的方法 - 它没有缓存 – user2003035 2014-09-29 12:59:19

+0

然后只需在Lazy声明期间调用该方法。 SEe编辑 – samy 2014-09-29 13:02:33

+0

我不需要将密钥传递给我的方法来调用,以及如何在我的常规函数​​中定义参数methodToCallWithParameter? – user2003035 2014-09-29 13:28:28

0

您可以轻松地沿线的扩展方法做到这一点:

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(); 
} 

但是,你真的不应该做到这一点;你会分享相同的锁定所有内容,这只会导致争用。

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

+0

你说得对,我会用锁的字典。但它不能解决我的问题,因为我需要有多次我的方法调用一次它是一个int,另一次是两个日期,... – user2003035 2014-09-29 13:04:24

+0

这就是我想要做的点,你'有一本关于锁的字典(这里的关键是什么,'Func'?没有任何作用,因为它是一个参考)。您希望保留与调用“GetDraw”,“GetDraws”等的锁定代码,您可以在其中锁定其他锁以实现所需的粒度。 – casperOne 2014-09-29 18:38:49

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; 
    }