From 6522193118c147ca3c34bc70b8bd4c8ebd777c9b Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Aug 2013 11:16:10 +1000 Subject: [PATCH] Simplifies the caching interfaces and updates the CacheHelper to expose the different caching levels (obsoletes it's normal methods) --- .../Cache/CacheProviderExtensions.cs | 31 +- .../Cache/HttpRuntimeCacheProvider.cs | 227 +++------- src/Umbraco.Core/Cache/ICacheProvider.cs | 5 +- .../Cache/IRuntimeCacheProvider.cs | 31 +- src/Umbraco.Core/Cache/NullCacheProvider.cs | 32 +- .../Cache/ObjectCacheRuntimeCacheProvider.cs | 129 +++--- src/Umbraco.Core/CacheHelper.cs | 401 ++++-------------- .../Cache/RuntimeCacheProviderTests.cs | 2 +- 8 files changed, 264 insertions(+), 594 deletions(-) diff --git a/src/Umbraco.Core/Cache/CacheProviderExtensions.cs b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs index 475e4adfaf..4a47e11f95 100644 --- a/src/Umbraco.Core/Cache/CacheProviderExtensions.cs +++ b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Web.Caching; namespace Umbraco.Core.Cache { @@ -9,10 +10,30 @@ namespace Umbraco.Core.Cache /// internal static class CacheProviderExtensions { - //T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); - //T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - //T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - //T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + public static T GetCacheItem(this IRuntimeCacheProvider provider, + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) + { + var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles); + return result == null ? default(T) : result.TryConvertTo().Result; + } + + public static void InsertCacheItem(this IRuntimeCacheProvider provider, + string cacheKey, + Func getCacheItem, + TimeSpan? timeout = null, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) + { + provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles); + } public static void ClearCacheObjectTypes(this ICacheProvider provider) { @@ -22,7 +43,7 @@ namespace Umbraco.Core.Cache public static IEnumerable GetCacheItemsByKeySearch(this ICacheProvider provider, string keyStartsWith) { var result = provider.GetCacheItemsByKeySearch(keyStartsWith); - return result.Select(x => ObjectExtensions.TryConvertTo(x).Result); + return result.Select(x => x.TryConvertTo().Result); } public static T GetCacheItem(this ICacheProvider provider, string cacheKey) diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 17cabbe903..b9d14276c9 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Threading; using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; @@ -13,7 +15,7 @@ namespace Umbraco.Core.Cache { private readonly System.Web.Caching.Cache _cache; private readonly DictionaryCacheWrapper _wrapper; - private static readonly object Locker = new object(); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache) { @@ -34,193 +36,86 @@ namespace Umbraco.Core.Cache /// public override object GetCacheItem(string cacheKey, Func getCacheItem) { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem, Locker); + return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null); } /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) + /// This overload is here for legacy purposes /// - /// /// - /// This will set an absolute expiration from now until the timeout /// - /// - public virtual TT GetCacheItem(string cacheKey, - TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, null, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemRemovedCallback refreshAction, TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// + /// + /// /// - /// - /// This will set an absolute expiration from now until the timeout - /// + /// + /// /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - public virtual TT GetCacheItem(string cacheKey, - CacheItemPriority priority, - CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem, Locker); - } - - /// - /// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW) - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - /// - /// - private TT GetCacheItem(string cacheKey, - CacheItemPriority priority, CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem, object syncLock) + internal object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null) { cacheKey = GetCacheKey(cacheKey); - var result = DictionaryCache.Get(cacheKey); - if (result == null) + using (var lck = new UpgradeableReadLock(Locker)) { - lock (syncLock) + var result = DictionaryCache.Get(cacheKey); + if (result == null) { - result = DictionaryCache.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, - timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - TimeSpan.Zero, priority, refreshAction); - } + lck.UpgradeToWriteLock(); + + result = getCacheItem(); + if (result != null) + { + var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); + + _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } + } + return result; } - return result.TryConvertTo().Result; } - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - Func getCacheItem) + public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { - InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - TimeSpan? timeout, - Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem); - } - - /// - /// Inserts an item into the cache, if it already exists in the cache it will be replaced - /// - /// - /// - /// - /// - /// - /// This will set an absolute expiration from now until the timeout - /// - public virtual void InsertCacheItem(string cacheKey, - CacheItemPriority priority, - CacheItemRemovedCallback refreshAction, - CacheDependency cacheDependency, - TimeSpan? timeout, - Func getCacheItem) - { - object result = getCacheItem(); - if (result != null) + CacheDependency dependency = null; + if (dependentFiles != null && dependentFiles.Any()) { - cacheKey = GetCacheKey(cacheKey); - - //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, - timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - TimeSpan.Zero, priority, refreshAction); + dependency = new CacheDependency(dependentFiles); } + return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency); } + /// + /// This overload is here for legacy purposes + /// + /// + /// + /// + /// + /// + /// + /// + internal void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null) + { + var result = getCacheItem(); + if (result == null) return; + + cacheKey = GetCacheKey(cacheKey); + + var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); + + _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); + } + + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + CacheDependency dependency = null; + if (dependentFiles != null && dependentFiles.Any()) + { + dependency = new CacheDependency(dependentFiles); + } + InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/ICacheProvider.cs b/src/Umbraco.Core/Cache/ICacheProvider.cs index 4c14083e1d..2aca849e7a 100644 --- a/src/Umbraco.Core/Cache/ICacheProvider.cs +++ b/src/Umbraco.Core/Cache/ICacheProvider.cs @@ -6,10 +6,7 @@ namespace Umbraco.Core.Cache /// /// An abstract class for implementing a basic cache provider /// - /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC... - /// - internal interface ICacheProvider + public interface ICacheProvider { void ClearAllCache(); void ClearCacheItem(string key); diff --git a/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs index 8dd1d31af0..b0227ca114 100644 --- a/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs @@ -1,6 +1,8 @@ using System; +using System.Runtime.Caching; using System.Text; using System.Web.Caching; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; namespace Umbraco.Core.Cache { @@ -8,17 +10,26 @@ namespace Umbraco.Core.Cache /// An abstract class for implementing a runtime cache provider /// /// - /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC... /// - internal interface IRuntimeCacheProvider : ICacheProvider + public interface IRuntimeCacheProvider : ICacheProvider { - T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); + object GetCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null); + + void InsertCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout = null, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null); + } } diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs index 6d1dc15e1f..d9d4a46347 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -42,40 +42,14 @@ namespace Umbraco.Core.Cache return getCacheItem(); } - public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { return getCacheItem(); } - public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - return getCacheItem(); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index b237e0c8c8..19c388bcbf 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache /// internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider { - private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); + private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); internal ObjectCache MemoryCache; public ObjectCacheRuntimeCacheProvider() @@ -26,7 +26,7 @@ namespace Umbraco.Core.Cache public virtual void ClearAllCache() { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { MemoryCache.DisposeIfDisposable(); MemoryCache = new MemoryCache("in-memory"); @@ -35,7 +35,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheItem(string key) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { if (MemoryCache[key] == null) return; MemoryCache.Remove(key); @@ -44,7 +44,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(string typeName) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { var keysToRemove = (from c in MemoryCache where c.Value.GetType().ToString().InvariantEquals(typeName) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -56,7 +56,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheByKeySearch(string keyStartsWith) { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); foreach (var k in keysToRemove) @@ -68,7 +68,7 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheByKeyExpression(string regexString) { - using (new WriteLock(ClearLock)) + 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) @@ -93,27 +93,19 @@ namespace Umbraco.Core.Cache public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem); + return GetCacheItem(cacheKey, getCacheItem, null); } - public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public object GetCacheItem( + string cacheKey, + Func getCacheItem, + TimeSpan? timeout, + bool isSliding = false, + CacheItemPriority priority = CacheItemPriority.Normal, + CacheItemRemovedCallback removedCallback = null, + string[] dependentFiles = null) { - return GetCacheItem(cacheKey, null, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) - { - return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem); - } - - public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - using (var lck = new UpgradeableReadLock(ClearLock)) + using (var lck = new UpgradeableReadLock(Locker)) { var result = MemoryCache.Get(cacheKey); if (result == null) @@ -123,57 +115,70 @@ namespace Umbraco.Core.Cache result = getCacheItem(); if (result != null) { - var policy = new CacheItemPolicy - { - AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - SlidingExpiration = TimeSpan.Zero - }; - - //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface - // to accept an array of files instead of CacheDependency. - //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); - + var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy); } } - return result.TryConvertTo().Result; + return result; } } - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) - { - InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem); - } - - public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { object result = getCacheItem(); if (result != null) { - - var policy = new CacheItemPolicy - { - AbsoluteExpiration = timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value), - SlidingExpiration = TimeSpan.Zero - }; - - //TODO: CUrrently we cannot implement this in this provider, we'll have to change the underlying interface - // to accept an array of files instead of CacheDependency. - //policy.ChangeMonitors.Add(new HostFileChangeMonitor(cacheDependency.)); - + var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles); MemoryCache.Set(cacheKey, result, policy); } } + + private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) + { + var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); + var sliding = isSliding == false ? ObjectCache.NoSlidingExpiration : (timeout ?? ObjectCache.NoSlidingExpiration); + + var policy = new CacheItemPolicy + { + AbsoluteExpiration = absolute, + SlidingExpiration = sliding + }; + + if (dependentFiles != null && dependentFiles.Any()) + { + policy.ChangeMonitors.Add(new HostFileChangeMonitor(dependentFiles.ToList())); + } + + if (removedCallback != null) + { + policy.RemovedCallback = arguments => + { + //convert the reason + var reason = CacheItemRemovedReason.Removed; + switch (arguments.RemovedReason) + { + case CacheEntryRemovedReason.Removed: + reason = CacheItemRemovedReason.Removed; + break; + case CacheEntryRemovedReason.Expired: + reason = CacheItemRemovedReason.Expired; + break; + case CacheEntryRemovedReason.Evicted: + reason = CacheItemRemovedReason.Underused; + break; + case CacheEntryRemovedReason.ChangeMonitorChanged: + reason = CacheItemRemovedReason.Expired; + break; + case CacheEntryRemovedReason.CacheSpecificEviction: + reason = CacheItemRemovedReason.Underused; + break; + } + //call the callback + removedCallback(arguments.CacheItem.Key, arguments.CacheItem.Value, reason); + }; + } + return policy; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 3be8e21f52..5cf0adb3da 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -52,309 +52,39 @@ namespace Umbraco.Core _requestCache = requestCacheProvider; } - #region Request cache - /// - /// Clears the item in umbraco's request cache + /// Returns the current Request cache /// - internal void ClearAllRequestCache() + public ICacheProvider RequestCache { - if (!_enableCache) - { - _nullRequestCache.ClearAllCache(); - } - else - { - _requestCache.ClearAllCache(); - } + get { return _enableCache ? _requestCache : _nullRequestCache; } } /// - /// Clears the item in umbraco's request cache with the given key + /// Returns the current Runtime cache /// - /// Key - internal void ClearRequestCacheItem(string key) + public ICacheProvider StaticCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheItem(key); - } - else - { - _requestCache.ClearCacheItem(key); - } + get { return _enableCache ? _staticCache : _nullStaticCache; } } /// - /// Clears all objects in the request cache with the System.Type name as the - /// input parameter. (using [object].GetType()) + /// Returns the current Runtime cache /// - /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - internal void ClearRequestCacheObjectTypes(string typeName) - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(typeName); - } - else - { - _requestCache.ClearCacheObjectTypes(typeName); - } - } - - /// - /// Clears all objects in the request cache with the System.Type specified - /// - internal void ClearRequestCacheObjectTypes() - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(); - } - else - { - _requestCache.ClearCacheObjectTypes(); - } - } - - /// - /// Clears all request cache items that starts with the key passed. - /// - /// The start of the key - internal void ClearRequestCacheByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheByKeySearch(keyStartsWith); - } - else - { - _requestCache.ClearCacheByKeySearch(keyStartsWith); - } - } - - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - internal void ClearRequestCacheByKeyExpression(string regexString) - { - if (!_enableCache) - { - _nullRequestCache.ClearCacheByKeyExpression(regexString); - } - else - { - _requestCache.ClearCacheByKeyExpression(regexString); - } - } - - internal IEnumerable GetRequestCacheItemsByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItemsByKeySearch(keyStartsWith); - } - else - { - return _requestCache.GetCacheItemsByKeySearch(keyStartsWith); - } - } - - /// - /// Returns a request cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - internal TT GetRequestCacheItem(string cacheKey) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItem(cacheKey); - } - else - { - return _requestCache.GetCacheItem(cacheKey); - } - } - - /// - /// Gets (and adds if necessary) an item from the request cache with all of the default parameters - /// - /// - /// - /// - /// - internal TT GetRequestCacheItem(string cacheKey, Func getCacheItem) - { - if (!_enableCache) - { - return _nullRequestCache.GetCacheItem(cacheKey, getCacheItem); - } - else - { - return _requestCache.GetCacheItem(cacheKey, getCacheItem); - } - } + public IRuntimeCacheProvider RuntimeCache + { + get { return _enableCache ? _httpCache : _nullHttpCache; } + } - #endregion + #region Legacy Runtime/Http Cache accessors - #region Static cache - - /// - /// Clears the item in umbraco's static cache - /// - internal void ClearAllStaticCache() - { - if (!_enableCache) - { - _nullStaticCache.ClearAllCache(); - } - else - { - _staticCache.ClearAllCache(); - } - } - - /// - /// Clears the item in umbraco's static cache with the given key - /// - /// Key - internal void ClearStaticCacheItem(string key) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheItem(key); - } - else - { - _staticCache.ClearCacheItem(key); - } - } - - /// - /// Clears all objects in the static 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" - internal void ClearStaticCacheObjectTypes(string typeName) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheObjectTypes(typeName); - } - else - { - _staticCache.ClearCacheObjectTypes(typeName); - } - } - - /// - /// Clears all objects in the static cache with the System.Type specified - /// - internal void ClearStaticCacheObjectTypes() - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheObjectTypes(); - } - else - { - _staticCache.ClearCacheObjectTypes(); - } - } - - /// - /// Clears all static cache items that starts with the key passed. - /// - /// The start of the key - internal void ClearStaticCacheByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheByKeySearch(keyStartsWith); - } - else - { - _staticCache.ClearCacheByKeySearch(keyStartsWith); - } - } - - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - internal void ClearStaticCacheByKeyExpression(string regexString) - { - if (!_enableCache) - { - _nullStaticCache.ClearCacheByKeyExpression(regexString); - } - else - { - _staticCache.ClearCacheByKeyExpression(regexString); - } - } - - internal IEnumerable GetStaticCacheItemsByKeySearch(string keyStartsWith) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItemsByKeySearch(keyStartsWith); - } - else - { - return _staticCache.GetCacheItemsByKeySearch(keyStartsWith); - } - } - - /// - /// Returns a static cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - internal TT GetStaticCacheItem(string cacheKey) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItem(cacheKey); - } - else - { - return _staticCache.GetCacheItem(cacheKey); - } - } - - /// - /// Gets (and adds if necessary) an item from the static cache with all of the default parameters - /// - /// - /// - /// - /// - internal TT GetStaticCacheItem(string cacheKey, Func getCacheItem) - { - if (!_enableCache) - { - return _nullStaticCache.GetCacheItem(cacheKey, getCacheItem); - } - else - { - return _staticCache.GetCacheItem(cacheKey, getCacheItem); - } - } - - #endregion - - #region Runtime/Http Cache /// /// Clears the item in umbraco's runtime cache /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearAllCache() { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearAllCache(); } @@ -368,9 +98,10 @@ namespace Umbraco.Core /// Clears the item in umbraco's runtime cache with the given key /// /// Key + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheItem(string key) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheItem(key); } @@ -386,9 +117,10 @@ namespace Umbraco.Core /// input parameter. (using [object].GetType()) /// /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheObjectTypes(string typeName) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheObjectTypes(typeName); } @@ -401,9 +133,10 @@ namespace Umbraco.Core /// /// Clears all objects in the System.Web.Cache with the System.Type specified /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheObjectTypes() { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheObjectTypes(); } @@ -417,9 +150,10 @@ namespace Umbraco.Core /// Clears all cache items that starts with the key passed. /// /// The start of the key + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheByKeySearch(string keyStartsWith) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheByKeySearch(keyStartsWith); } @@ -433,9 +167,10 @@ namespace Umbraco.Core /// Clears all cache items that have a key that matches the regular expression /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public void ClearCacheByKeyExpression(string regexString) { - if (!_enableCache) + if (_enableCache == false) { _nullHttpCache.ClearCacheByKeyExpression(regexString); } @@ -445,9 +180,10 @@ namespace Umbraco.Core } } + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItemsByKeySearch(keyStartsWith); } @@ -463,9 +199,10 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItem(cacheKey); } @@ -482,9 +219,10 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem); } @@ -502,16 +240,17 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout); } else { - return _httpCache.GetCacheItem(cacheKey, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout); } } @@ -524,17 +263,18 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { if (!_enableCache) { - return _nullHttpCache.GetCacheItem(cacheKey, refreshAction, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction); } else { - return _httpCache.GetCacheItem(cacheKey, refreshAction, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, removedCallback: refreshAction); } } @@ -548,17 +288,18 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, refreshAction, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction); } else { - return _httpCache.GetCacheItem(cacheKey, priority, refreshAction, timeout, getCacheItem); + return _httpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction); } } @@ -573,6 +314,7 @@ namespace Umbraco.Core /// This will set an absolute expiration from now until the timeout /// /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, @@ -580,13 +322,19 @@ namespace Umbraco.Core TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null); } else { - return _httpCache.GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency); + return result == null ? default(TT) : result.TryConvertTo().Result; + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -599,6 +347,7 @@ namespace Umbraco.Core /// /// /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, @@ -606,11 +355,17 @@ namespace Umbraco.Core { if (!_enableCache) { - return _nullHttpCache.GetCacheItem(cacheKey, priority, null, cacheDependency, null, getCacheItem); + return _nullHttpCache.GetCacheItem(cacheKey, getCacheItem, null, false, priority, null, null); } else { - return _httpCache.GetCacheItem(cacheKey, priority, null, cacheDependency, null, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency); + return result == null ? default(TT) : result.TryConvertTo().Result; + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -625,13 +380,13 @@ namespace Umbraco.Core CacheItemPriority priority, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority); } else { - _httpCache.InsertCacheItem(cacheKey, priority, getCacheItem); + _httpCache.InsertCacheItem(cacheKey, getCacheItem, priority: priority); } } @@ -648,13 +403,13 @@ namespace Umbraco.Core TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority); } else { - _httpCache.InsertCacheItem(cacheKey, priority, timeout, getCacheItem); + _httpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority); } } @@ -667,19 +422,25 @@ namespace Umbraco.Core /// /// This will set an absolute expiration from now until the timeout /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, cacheDependency, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles:null); } else { - _httpCache.InsertCacheItem(cacheKey, priority, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency); + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } @@ -693,6 +454,7 @@ namespace Umbraco.Core /// /// This will set an absolute expiration from now until the timeout /// + [Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")] public void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, @@ -700,13 +462,18 @@ namespace Umbraco.Core TimeSpan? timeout, Func getCacheItem) { - if (!_enableCache) + if (_enableCache == false) { - _nullHttpCache.InsertCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + _nullHttpCache.InsertCacheItem(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null); } else { - _httpCache.InsertCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem); + var cache = _httpCache as HttpRuntimeCacheProvider; + if (cache != null) + { + cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency); + } + throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider)); } } #endregion diff --git a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs index 03ad355f42..d123d2dc31 100644 --- a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Cache public void Can_Add_And_Expire_Struct_Strongly_Typed_With_Null() { var now = DateTime.Now; - RuntimeProvider.InsertCacheItem("DateTimeTest", CacheItemPriority.Default, new TimeSpan(0, 0, 0, 0, 200), () => now); + RuntimeProvider.InsertCacheItem("DateTimeTest", () => now, new TimeSpan(0, 0, 0, 0, 200)); Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest"));