/// <summary> /// Core cache proc method [low level]. /// Will try to find result in cache based on key, else will call the func and store result in cache attached to the key. /// </summary> /// <typeparam name="R">Function result type</typeparam> /// <param name="Key">Key to asssociate with the result</param> /// <param name="Factory">Function to produce the result</param> /// <returns>Function call result ether actual or cached</returns> protected R GetOrMakeWithKey <R>(Object Key, Func <R> Factory) { Interlocked.Increment(ref CacheStatUseCount); CacheItem CacheEntry; FuncCallResult <R> CachedResult = null; lock (Cache) { if (Cache.TryGetValue(Key, out CacheEntry)) { CachedResult = CacheEntry.CallResult as FuncCallResult <R>; if (CachedResult != null) { // Found and type is valid if (isExpiried(CacheEntry)) { CachedResult = null; Cache.Remove(Key); // expiried entry CacheEntry = null; } } else { Cache.Remove(Key); // invalid entry (bug trap) CacheEntry = null; } } else { CacheEntry = null; } } if (CachedResult == null) { // not found in cache CachedResult = FuncCall.Call(Factory); // That may take long time and we are not locked var Now = GetLongTickCount(); CacheEntry = new CacheItem { MakeTimestamp = Now, LastAccessTimestamp = Now, CallResult = CachedResult }; CacheStatLastAccessTimestamp = Now; lock (Cache) { try { Cache.Add(Key, CacheEntry); } catch { // Someone already added result (earlier) try { // Refresh Cache.Remove(Key); Cache.Add(Key, CacheEntry); } catch { // Failed to remove/add? // This should not be a case since we are in lock throw; } } } } else { // Found in cache var Now = GetLongTickCount(); Interlocked.Increment(ref CacheEntry.HitCount); CacheEntry.LastAccessTimestamp = Now; Interlocked.Increment(ref CacheStatHitCount); CacheStatLastAccessTimestamp = Now; } if (CachedResult.Exception != null) { throw CachedResult.Exception; // rethrow } return(CachedResult.Result); }