using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; namespace Umbraco.Core { /// /// Class that is exposed by the ApplicationContext for application wide caching purposes /// /// /// This class may be opened publicly at some point but needs a review of what is absoletely necessary. /// public class CacheHelper { private readonly Cache _cache; public CacheHelper(System.Web.Caching.Cache cache) { _cache = cache; } private static readonly object Locker = new object(); /// /// Clears everything in umbraco's runtime cache, which means that not only /// umbraco content is removed, but also other cache items from pages running in /// the same application / website. Use with care :-) /// public void ClearAllCache() { var cacheEnumerator = _cache.GetEnumerator(); while (cacheEnumerator.MoveNext()) { _cache.Remove(cacheEnumerator.Key.ToString()); } } /// /// Clears the item in umbraco's runtime cache with the given key /// /// Key public void ClearCacheItem(string key) { // NH 10 jan 2012 // Patch by the always wonderful Stéphane Gay to avoid cache null refs lock (Locker) { if (_cache[key] == null) return; _cache.Remove(key);; } } /// /// 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 void ClearCacheObjectTypes(string typeName) { try { lock (Locker) { foreach (var c in from DictionaryEntry c in _cache where _cache[c.Key.ToString()] != null && _cache[c.Key.ToString()].GetType().ToString() == typeName select c) { _cache.Remove(c.Key.ToString()); } } } catch (Exception e) { LogHelper.Error("Cache clearing error", e); } } /// /// Clears all cache items that starts with the key passed. /// /// The start of the key public void ClearCacheByKeySearch(string keyStartsWith) { foreach (var c in from DictionaryEntry c in _cache where c.Key is string && ((string)c.Key).StartsWith(keyStartsWith) select c) { ClearCacheItem((string)c.Key); } } public TT GetCacheItem(string cacheKey, TimeSpan timeout, Func getCacheItem) { return GetCacheItem(cacheKey, null, timeout, getCacheItem); } public TT GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); } public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); } public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan timeout, Func getCacheItem) { return GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem, Locker); } /// /// This is used only for legacy purposes as I did not want to change all of the locking to one lock found on this object, /// however, the reason this is used for legacy purposes is because I see zero reason to use different sync locks, just the one /// lock (Locker) on this class should be sufficient. /// /// /// /// /// /// /// /// /// /// internal TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan timeout, Func getCacheItem, object syncLock) { var result = _cache.Get(cacheKey); if (result == null) { lock (syncLock) { result = _cache.Get(cacheKey); if (result == null) { result = getCacheItem(); if (result != null) { //we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it. _cache.Insert(cacheKey, result, cacheDependency, DateTime.Now.Add(timeout), TimeSpan.Zero, priority, refreshAction); } } } } return (TT)result; } } }