using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading; namespace Umbraco.Core.Cache { internal abstract class DictionaryCacheProviderBase : ICacheProvider { protected readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); protected abstract DictionaryCacheWrapper DictionaryCache { get; } /// /// Clears everything in umbraco's runtime cache /// /// /// Does not clear other stuff the user has put in httpruntime.cache! /// public virtual void ClearAllCache() { using (new WriteLock(Locker)) { var keysToRemove = DictionaryCache.Cast() .Select(item => new DictionaryItemWrapper(item)) .Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null) .Select(c => c.Key) .ToList(); foreach (var k in keysToRemove) { DictionaryCache.Remove(k); } } } /// /// Clears the item in umbraco's runtime cache with the given key /// /// Key public virtual void ClearCacheItem(string key) { using (new WriteLock(Locker)) { var cacheKey = GetCacheKey(key); if (DictionaryCache[cacheKey] == null) return; DictionaryCache.Remove(cacheKey); } } /// /// Clears all objects in the System.Web.Cache with the System.Type name as the /// input parameter. (using [object].GetType()) /// /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" public virtual void ClearCacheObjectTypes(string typeName) { using (new WriteLock(Locker)) { var keysToRemove = DictionaryCache .Cast() .Select(item => new DictionaryItemWrapper(item)) .Where(c => { var k = c.Key.ToString(); var v = DictionaryCache[k]; return v != null && v.GetType().ToString().InvariantEquals(typeName); }) .Select(c => c.Key) .ToList(); foreach (var k in keysToRemove) DictionaryCache.Remove(k); } } public virtual void ClearCacheObjectTypes() { using (new WriteLock(Locker)) { var typeOfT = typeof(T); var keysToRemove = DictionaryCache .Cast() .Select(item => new DictionaryItemWrapper(item)) .Where(c => { var k = c.Key.ToString(); var v = DictionaryCache[k]; return v != null && v.GetType() == typeOfT; }) .Select(c => c.Key) .ToList(); foreach (var k in keysToRemove) DictionaryCache.Remove(k); } } public virtual void ClearCacheObjectTypes(Func predicate) { using (new WriteLock(Locker)) { var typeOfT = typeof(T); var keysToRemove = DictionaryCache .Cast() .Select(item => new DictionaryItemWrapper(item)) .Where(c => { var k = c.Key.ToString(); var v = DictionaryCache[k]; return v != null && v.GetType() == typeOfT && predicate(k, (T)v); }) .Select(c => c.Key) .ToList(); foreach (var k in keysToRemove) DictionaryCache.Remove(k); } } /// /// Clears all cache items that starts with the key passed. /// /// The start of the key public virtual void ClearCacheByKeySearch(string keyStartsWith) { using (new WriteLock(Locker)) { var keysToRemove = DictionaryCache.Cast() .Select(item => new DictionaryItemWrapper(item)) .Where( c => c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) .Select(c => c.Key) .ToList(); foreach (var k in keysToRemove) { DictionaryCache.Remove(k); } } } /// /// Clears all cache items that have a key that matches the regular expression /// /// public virtual void ClearCacheByKeyExpression(string regexString) { using (new WriteLock(Locker)) { var keysToRemove = new List(); foreach (var item in DictionaryCache) { var c = new DictionaryItemWrapper(item); var s = c.Key as string; if (s != null) { var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); if (Regex.IsMatch(withoutPrefix, regexString)) { keysToRemove.Add(c.Key); } } } foreach (var k in keysToRemove) { DictionaryCache.Remove(k); } } } public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { using (new ReadLock(Locker)) { return (from object item in DictionaryCache select new DictionaryItemWrapper(item) into c where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)) select c.Value).ToList(); } } public virtual IEnumerable GetCacheItemsByKeyExpression(string regexString) { using (new ReadLock(Locker)) { var found = new List(); foreach (var item in DictionaryCache) { var c = new DictionaryItemWrapper(item); var s = c.Key as string; if (s != null) { var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); if (Regex.IsMatch(withoutPrefix, regexString)) { found.Add(c.Value); } } } return found; } } /// /// Returns a cache item by key, does not update the cache if it isn't there. /// /// /// public virtual object GetCacheItem(string cacheKey) { using (new ReadLock(Locker)) { var result = DictionaryCache.Get(GetCacheKey(cacheKey)); return result; } } public abstract object GetCacheItem(string cacheKey, Func getCacheItem); /// /// We prefix all cache keys with this so that we know which ones this class has created when /// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create. /// protected const string CacheItemPrefix = "umbrtmche"; protected string GetCacheKey(string key) { return string.Format("{0}-{1}", CacheItemPrefix, key); } } }