2014-09-29 151 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; 
    }