private static TItem GetInternal <TItem, TSerialized>(string cacheKey, TimeSpan localExpiration, TimeSpan remoteExpiration, string groupKey, Func <TItem> loader, Func <TItem, TSerialized> serialize, Func <TSerialized, TItem> deserialize) where TItem : class where TSerialized : class { ulong?groupGeneration = null; ulong?groupGenerationCache = null; string itemGenerationKey = cacheKey + GenerationSuffix; var localCache = LocalCache.Provider; var distributedCache = DistributedCache.Provider; // retrieves distributed cache group generation number lazily Func <ulong> getGroupGenerationValue = delegate() { if (groupGeneration != null) { return(groupGeneration.Value); } groupGeneration = distributedCache.Get <ulong?>(groupKey); if (groupGeneration == null || groupGeneration == 0) { groupGeneration = RandomGeneration(); distributedCache.Set(groupKey, groupGeneration.Value); } groupGenerationCache = groupGeneration.Value; // add to local cache, use 5 seconds from there LocalCache.Add(groupKey, groupGenerationCache, GenerationCacheExpiration); return(groupGeneration.Value); }; // retrieves local cache group generation number lazily Func <ulong> getGroupGenerationCacheValue = delegate() { if (groupGenerationCache != null) { return(groupGenerationCache.Value); } // check cached local value of group key // it expires in 5 seconds and read from server again groupGenerationCache = localCache.Get <object>(groupKey) as ulong?; // if its in local cache, return it if (groupGenerationCache != null) { return(groupGenerationCache.Value); } return(getGroupGenerationValue()); }; // first check local cache, if item exists and not expired (group version = item version) return it var cachedObj = localCache.Get <object>(cacheKey); if (cachedObj != null) { // check local cache, if exists, compare version with group one var itemGenerationCache = localCache.Get <object>(itemGenerationKey) as ulong?; if (itemGenerationCache != null && itemGenerationCache == getGroupGenerationCacheValue()) { // local cached item is not expired yet if (cachedObj == DBNull.Value) { return(null); } return((TItem)cachedObj); } // local cached item is expired, remove all information if (itemGenerationCache != null) { localCache.Remove(itemGenerationKey); } localCache.Remove(cacheKey); cachedObj = null; } // if serializer is null, than this is a local store only item if (serialize != null) { // no item in local cache or expired, now check distributed cache var itemGeneration = distributedCache.Get <ulong?>(itemGenerationKey); // if item has version number in distributed cache and this is equal to group version if (itemGeneration != null && itemGeneration.Value == getGroupGenerationValue()) { // get item from distributed cache var serialized = distributedCache.Get <TSerialized>(cacheKey); // if item exists in distributed cache if (serialized != null) { cachedObj = deserialize(serialized); LocalCache.Add(cacheKey, (object)cachedObj ?? DBNull.Value, localExpiration); LocalCache.Add(itemGenerationKey, getGroupGenerationValue(), localExpiration); return((TItem)cachedObj); } } } // couldn't find valid item in local or distributed cache, produce value by calling loader var item = loader(); // add item and its version to cache LocalCache.Add(cacheKey, (object)item ?? DBNull.Value, localExpiration); LocalCache.Add(itemGenerationKey, getGroupGenerationValue(), localExpiration); if (serialize != null) { var serializedItem = serialize(item); // add item and generation to distributed cache if (remoteExpiration == TimeSpan.Zero) { distributedCache.Set(cacheKey, serializedItem); distributedCache.Set(itemGenerationKey, getGroupGenerationValue()); } else { distributedCache.Set(cacheKey, serializedItem, remoteExpiration); distributedCache.Set(itemGenerationKey, getGroupGenerationValue(), remoteExpiration); } } return(item); }