diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 0813a113c6..c4f14fdeec 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -30,29 +30,11 @@ namespace Umbraco.Core.Cache get { return _wrapper; } } - // don't use, _cache enumerates DictionaryEntry items - // - //private IEnumerable> EnumerateDictionaryCache() - //{ - // // DictionaryCache just wraps _cache which has a special enumerator - // var enumerator = _cache.GetEnumerator(); - // while (enumerator.MoveNext()) - // { - // var key = enumerator.Key as string; - // if (key == null) continue; - // yield return new KeyValuePair(key, enumerator.Value); - // } - //} - public override void ClearAllCache() { using (new WriteLock(Locker)) { - // remove null values as well! - - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).StartsWith(CacheItemPrefix) /* && x.Value != null */)) + foreach (var entry in GetDictionaryEntries()) _cache.Remove((string)entry.Key); } } @@ -68,14 +50,15 @@ namespace Umbraco.Core.Cache public override void ClearCacheObjectTypes(string typeName) { - var typeName2 = typeName; using (new WriteLock(Locker)) { - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).StartsWith(CacheItemPrefix) - && x.Value != null - && x.Value.GetType().ToString().InvariantEquals(typeName2))) + foreach (var entry in GetDictionaryEntries() + .Where(x => + { + // entry.Value is Lazy and not null, its value may be null + var value = ((Lazy) x.Value).Value; + return value == null || value.GetType().ToString().InvariantEquals(typeName); // remove null values as well + })) _cache.Remove((string)entry.Key); } } @@ -87,12 +70,13 @@ namespace Umbraco.Core.Cache var typeOfT = typeof(T); using (new WriteLock(Locker)) { - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).StartsWith(CacheItemPrefix) - && x.Value != null - && x.Value.GetType() == typeOfT)) - //&& x.Value is T)) + foreach (var entry in GetDictionaryEntries() + .Where(x => + { + // entry.Value is Lazy and not null, its value may be null + var value = ((Lazy)x.Value).Value; + return value == null || value.GetType() == typeOfT; // remove null values as well + })) _cache.Remove((string)entry.Key); } } @@ -102,57 +86,59 @@ namespace Umbraco.Core.Cache // note: compare on exact type, don't use "is" var typeOfT = typeof(T); - try + var plen = CacheItemPrefix.Length + 1; + using (new WriteLock(Locker)) { - using (new WriteLock(Locker)) - { - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).StartsWith(CacheItemPrefix) - && x.Value.GetType() == typeOfT // false if x.Value is null - //&& x.Value is T - && predicate((string)x.Key, (T) x.Value))) - _cache.Remove((string)entry.Key); - } - } - catch (Exception e) - { - // oops, what is this?! - LogHelper.Error("Cache clearing error", e); + foreach (var entry in GetDictionaryEntries() + .Where(x => + { + // entry.Value is Lazy and not null, its value may be null + var value = ((Lazy)x.Value).Value; + if (value == null) return true; // remove null values as well + return value.GetType() == typeOfT + // run predicate on the 'public key' part only, ie without prefix + && predicate(((string) x.Key).Substring(plen), (T) value); + })) + _cache.Remove((string)entry.Key); } } public override void ClearCacheByKeySearch(string keyStartsWith) { + var plen = CacheItemPrefix.Length + 1; using (new WriteLock(Locker)) { - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))) + foreach (var entry in GetDictionaryEntries() + .Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))) _cache.Remove((string)entry.Key); } } public override void ClearCacheByKeyExpression(string regexString) { - var plen = CacheItemPrefix.Length + 1; // string.Format("{0}-", CacheItemPrefix) + var plen = CacheItemPrefix.Length + 1; using (new WriteLock(Locker)) { - foreach (var entry in _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).StartsWith(CacheItemPrefix) - && Regex.IsMatch(((string)x.Key).Substring(plen), regexString))) + foreach (var entry in GetDictionaryEntries() + .Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))) _cache.Remove((string)entry.Key); } } + private IEnumerable GetDictionaryEntries() + { + const string prefix = CacheItemPrefix + "-"; + return _cache.Cast() + .Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix)); + } + public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { + var plen = CacheItemPrefix.Length + 1; using (new ReadLock(Locker)) { - return _cache.Cast() - .Where(x => x.Key is string - && ((string)x.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) + return GetDictionaryEntries() + .Where(x => ((string) x.Key).Substring(plen).InvariantStartsWith(keyStartsWith)) .Select(x => ((Lazy)x.Value).Value) .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); @@ -161,14 +147,13 @@ namespace Umbraco.Core.Cache public override IEnumerable GetCacheItemsByKeyExpression(string regexString) { - var plen = CacheItemPrefix.Length + 1; // string.Format("{0}-", CacheItemPrefix) + const string prefix = CacheItemPrefix + "-"; + var plen = prefix.Length; using (new ReadLock(Locker)) { - return _cache.Cast() - .Where(x => x.Key is string - && ((string) x.Key).StartsWith(CacheItemPrefix) - && Regex.IsMatch(((string) x.Key).Substring(plen), regexString)) - .Select(x => ((Lazy) x.Value).Value) + return GetDictionaryEntries() + .Where(x => Regex.IsMatch(((string) x.Key).Substring(plen), regexString)) + .Select(x => ((Lazy)x.Value).Value) .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); } diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index f2e6be47f5..1153fe54a3 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -46,12 +46,16 @@ namespace Umbraco.Core.Cache { using (new WriteLock(_locker)) { - var keysToRemove = MemoryCache - .Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName)) - .Select(c => c.Key) - .ToArray(); - foreach (var k in keysToRemove) - MemoryCache.Remove(k); + foreach (var key in MemoryCache + .Where(x => + { + // x.Value is Lazy and not null, its value may be null + var value = ((Lazy) x.Value).Value; + return value == null || value.GetType().ToString().InvariantEquals(typeName); // remove null values as well + }) + .Select(x => x.Key) + .ToArray()) // ToArray required to remove + MemoryCache.Remove(key); } } @@ -60,12 +64,16 @@ namespace Umbraco.Core.Cache using (new WriteLock(_locker)) { var typeOfT = typeof (T); - var keysToRemove = MemoryCache - .Where(c => c.Value != null && c.Value.GetType() == typeOfT) - .Select(c => c.Key) - .ToArray(); - foreach (var k in keysToRemove) - MemoryCache.Remove(k); + foreach (var key in MemoryCache + .Where(x => + { + // x.Value is Lazy and not null, its value may be null + var value = ((Lazy) x.Value).Value; + return value == null || value.GetType() == typeOfT; // remove null values as well + }) + .Select(x => x.Key) + .ToArray()) // ToArray required to remove + MemoryCache.Remove(key); } } @@ -74,12 +82,18 @@ namespace Umbraco.Core.Cache using (new WriteLock(_locker)) { var typeOfT = typeof(T); - var keysToRemove = MemoryCache - .Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value)) - .Select(c => c.Key) - .ToArray(); - foreach (var k in keysToRemove) - MemoryCache.Remove(k); + foreach (var key in MemoryCache + .Where(x => + { + // x.Value is Lazy and not null, its value may be null + var value = ((Lazy) x.Value).Value; + if (value == null) return true; // remove null values as well + return value.GetType() == typeOfT + && predicate(x.Key, (T) value); + }) + .Select(x => x.Key) + .ToArray()) // ToArray required to remove + MemoryCache.Remove(key); } } @@ -87,11 +101,11 @@ namespace Umbraco.Core.Cache { using (new WriteLock(_locker)) { - var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); - foreach (var k in keysToRemove) - { - MemoryCache.Remove(k); - } + foreach (var key in MemoryCache + .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) + .Select(x => x.Key) + .ToArray()) // ToArray required to remove + MemoryCache.Remove(key); } } @@ -99,11 +113,11 @@ namespace Umbraco.Core.Cache { using (new WriteLock(_locker)) { - var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList(); - foreach (var k in keysToRemove) - { - MemoryCache.Remove(k); - } + foreach (var key in MemoryCache + .Where(x => Regex.IsMatch(x.Key, regexString)) + .Select(x => x.Key) + .ToArray()) // ToArray required to remove + MemoryCache.Remove(key); } } @@ -114,6 +128,7 @@ namespace Umbraco.Core.Cache return MemoryCache .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) .Select(x => ((Lazy) x.Value).Value) + .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); } } @@ -125,6 +140,7 @@ namespace Umbraco.Core.Cache return MemoryCache .Where(x => Regex.IsMatch(x.Key, regexString)) .Select(x => ((Lazy) x.Value).Value) + .Where(x => x != null) // backward compat, don't store null values in the cache .ToList(); } } @@ -133,8 +149,8 @@ namespace Umbraco.Core.Cache { using (new ReadLock(_locker)) { - var result = MemoryCache.Get(cacheKey); - return result; + var result = MemoryCache.Get(cacheKey) as Lazy; + return result == null ? null : result.Value; } } @@ -159,7 +175,7 @@ namespace Umbraco.Core.Cache using (var lck = new UpgradeableReadLock(_locker)) { result = MemoryCache.Get(cacheKey) as Lazy; - if (result == null /* || (result.IsValueCreated && result.Value == null) */) + if (result == null || (result.IsValueCreated && result.Value == null)) { lck.UpgradeToWriteLock(); @@ -174,11 +190,12 @@ namespace Umbraco.Core.Cache public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { - // see notes in HttpRuntimeCacheProvider + // NOTE - here also we must insert a Lazy but we can evaluate it right now + // and make sure we don't store a null value. var result = new Lazy(getCacheItem); var value = result.Value; // force evaluation now - //if (value == null) return; + if (value == null) return; // do not store null values (backward compat) var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy);