diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 33748ae46b..a3f069ac7a 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -12,7 +12,7 @@ using Umbraco.Core.Services; namespace Umbraco.Core { - /// + /// /// the Umbraco Application context /// /// @@ -20,61 +20,45 @@ namespace Umbraco.Core /// public class ApplicationContext : IDisposable { - /// - /// Constructor - /// - internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) - : this(dbContext, serviceContext, true) - { - - } /// /// Constructor /// /// /// - /// - public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache) - : this(enableCache) + /// + public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache) { + if (dbContext == null) throw new ArgumentNullException("dbContext"); + if (serviceContext == null) throw new ArgumentNullException("serviceContext"); + if (cache == null) throw new ArgumentNullException("cache"); _databaseContext = dbContext; - _services = serviceContext; + _services = serviceContext; + ApplicationCache = cache; } - /// - /// Empty constructor normally reserved for unit tests when a DatabaseContext or a ServiceContext is not - /// necessarily required or needs to be set after construction. - /// - internal ApplicationContext() : this(true) - { - } - /// - /// Constructor used to specify if we will enable application cache or not + /// Creates a basic app context /// - /// - public ApplicationContext(bool enableCache) + /// + public ApplicationContext(CacheHelper cache) { - //create a new application cache from the HttpRuntime.Cache - ApplicationCache = HttpRuntime.Cache == null - ? new CacheHelper(new System.Web.Caching.Cache(), enableCache) - : new CacheHelper(HttpRuntime.Cache, enableCache); + ApplicationCache = cache; } - /// - /// A method used to set and/or ensure that a global ApplicationContext singleton is created. - /// - /// - /// The instance to set on the global application singleton - /// - /// If set to true and the singleton is already set, it will be replaced - /// - /// - /// This is NOT thread safe - /// - public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) - { + /// + /// A method used to set and/or ensure that a global ApplicationContext singleton is created. + /// + /// + /// The instance to set on the global application singleton + /// + /// If set to true and the singleton is already set, it will be replaced + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) + { if (ApplicationContext.Current != null) { if (!replaceContext) @@ -82,56 +66,56 @@ namespace Umbraco.Core } ApplicationContext.Current = appContext; return ApplicationContext.Current; - } + } - /// - /// A method used to create and ensure that a global ApplicationContext singleton is created. - /// - /// - /// - /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app - /// startup if for some reason the boot manager is not the umbraco boot manager. - /// - /// - /// - /// - /// - /// This is NOT thread safe - /// - public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache, bool replaceContext) + /// + /// A method used to create and ensure that a global ApplicationContext singleton is created. + /// + /// + /// + /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app + /// startup if for some reason the boot manager is not the umbraco boot manager. + /// + /// + /// + /// + /// + /// This is NOT thread safe + /// + public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext) { if (ApplicationContext.Current != null) { if (!replaceContext) return ApplicationContext.Current; } - var ctx = new ApplicationContext(dbContext, serviceContext, enableCache); + var ctx = new ApplicationContext(dbContext, serviceContext, cache); ApplicationContext.Current = ctx; return ApplicationContext.Current; } - /// - /// Singleton accessor - /// - public static ApplicationContext Current { get; internal set; } + /// + /// Singleton accessor + /// + public static ApplicationContext Current { get; internal set; } - /// - /// Returns the application wide cache accessor - /// - /// - /// Any caching that is done in the application (app wide) should be done through this property - /// - public CacheHelper ApplicationCache { get; private set; } + /// + /// Returns the application wide cache accessor + /// + /// + /// Any caching that is done in the application (app wide) should be done through this property + /// + public CacheHelper ApplicationCache { get; private set; } - // IsReady is set to true by the boot manager once it has successfully booted + // IsReady is set to true by the boot manager once it has successfully booted // note - the original umbraco module checks on content.Instance in umbraco.dll // now, the boot task that setup the content store ensures that it is ready bool _isReady = false; - readonly System.Threading.ManualResetEventSlim _isReadyEvent = new System.Threading.ManualResetEventSlim(false); - private DatabaseContext _databaseContext; - private ServiceContext _services; + readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false); + private DatabaseContext _databaseContext; + private ServiceContext _services; - public bool IsReady + public bool IsReady { get { @@ -141,14 +125,14 @@ namespace Umbraco.Core { AssertIsNotReady(); _isReady = value; - _isReadyEvent.Set(); + _isReadyEvent.Set(); } } - public bool WaitForReady(int timeout) - { - return _isReadyEvent.WaitHandle.WaitOne(timeout); - } + public bool WaitForReady(int timeout) + { + return _isReadyEvent.WaitHandle.WaitOne(timeout); + } // notes @@ -157,14 +141,14 @@ namespace Umbraco.Core // the system is configured if they match // if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus // - // then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... + // then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... SD: I have actually remove that now! // public bool IsConfigured { // todo - we should not do this - ok for now get { - return Configured; + return Configured; } } @@ -180,45 +164,45 @@ namespace Umbraco.Core /// internal string OriginalRequestUrl { get; set; } - private bool Configured - { - get - { - try - { - string configStatus = ConfigurationStatus; - string currentVersion = UmbracoVersion.Current.ToString(3); + private bool Configured + { + get + { + try + { + string configStatus = ConfigurationStatus; + string currentVersion = UmbracoVersion.Current.ToString(3); - if (currentVersion != configStatus) - { - LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); - } - + if (currentVersion != configStatus) + { + LogHelper.Info("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'"); + } - return (configStatus == currentVersion); - } - catch - { - return false; - } - } - } - private string ConfigurationStatus - { - get - { - try - { - return ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; - } - catch - { - return String.Empty; - } - } - } + return (configStatus == currentVersion); + } + catch + { + return false; + } + } + } + + private string ConfigurationStatus + { + get + { + try + { + return ConfigurationManager.AppSettings["umbracoConfigurationStatus"]; + } + catch + { + return String.Empty; + } + } + } private void AssertIsReady() { @@ -232,39 +216,39 @@ namespace Umbraco.Core throw new Exception("ApplicationContext has already been initialized."); } - /// - /// Gets the current DatabaseContext - /// - /// - /// Internal set is generally only used for unit tests - /// - public DatabaseContext DatabaseContext - { - get - { - if (_databaseContext == null) - throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext"); - return _databaseContext; - } - internal set { _databaseContext = value; } - } - - /// - /// Gets the current ServiceContext - /// - /// - /// Internal set is generally only used for unit tests - /// - public ServiceContext Services - { - get - { - if (_services == null) - throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext"); - return _services; - } - internal set { _services = value; } - } + /// + /// Gets the current DatabaseContext + /// + /// + /// Internal set is generally only used for unit tests + /// + public DatabaseContext DatabaseContext + { + get + { + if (_databaseContext == null) + throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext"); + return _databaseContext; + } + internal set { _databaseContext = value; } + } + + /// + /// Gets the current ServiceContext + /// + /// + /// Internal set is generally only used for unit tests + /// + public ServiceContext Services + { + get + { + if (_services == null) + throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext"); + return _services; + } + internal set { _services = value; } + } private volatile bool _disposed; @@ -290,26 +274,26 @@ namespace Umbraco.Core //clear the cache if (ApplicationCache != null) { - ApplicationCache.ClearAllCache(); + ApplicationCache.ClearAllCache(); } //reset all resolvers ResolverCollection.ResetAll(); //reset resolution itself (though this should be taken care of by resetting any of the resolvers above) Resolution.Reset(); - + //reset the instance objects this.ApplicationCache = null; if (_databaseContext != null) //need to check the internal field here { if (DatabaseContext.IsDatabaseConfigured) { - DatabaseContext.Database.Dispose(); - } + DatabaseContext.Database.Dispose(); + } } this.DatabaseContext = null; this.Services = null; this._isReady = false; //set the internal field - + // Indicate that the instance has been disposed. _disposed = true; } diff --git a/src/Umbraco.Core/Cache/CacheProviderExtensions.cs b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs new file mode 100644 index 0000000000..7b621d013f --- /dev/null +++ b/src/Umbraco.Core/Cache/CacheProviderExtensions.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Caching; + +namespace Umbraco.Core.Cache +{ + /// + /// Extensions for strongly typed access + /// + internal static class CacheProviderExtensions + { + 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 IEnumerable GetCacheItemsByKeySearch(this ICacheProvider provider, string keyStartsWith) + { + var result = provider.GetCacheItemsByKeySearch(keyStartsWith); + return result.Select(x => x.TryConvertTo().Result); + } + + public static T GetCacheItem(this ICacheProvider provider, string cacheKey) + { + var result = provider.GetCacheItem(cacheKey); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + + public static T GetCacheItem(this ICacheProvider provider, string cacheKey, Func getCacheItem) + { + var result = provider.GetCacheItem(cacheKey, () => getCacheItem()); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs index adc02f9104..c1a4d17592 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; namespace Umbraco.Core.Cache { internal abstract class DictionaryCacheProdiverBase : ICacheProvider { - private static readonly object Locker = new object(); + protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); protected abstract DictionaryCacheWrapper DictionaryCache { get; } /// @@ -18,7 +19,7 @@ namespace Umbraco.Core.Cache /// public virtual void ClearAllCache() { - lock (Locker) + using (new WriteLock(Locker)) { var keysToRemove = DictionaryCache.Cast() .Select(item => new DictionaryItemWrapper(item)) @@ -39,7 +40,7 @@ namespace Umbraco.Core.Cache /// Key public virtual void ClearCacheItem(string key) { - lock (Locker) + using (new WriteLock(Locker)) { if (DictionaryCache[GetCacheKey(key)] == null) return; DictionaryCache.Remove(GetCacheKey(key)); ; @@ -53,39 +54,66 @@ namespace Umbraco.Core.Cache /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" public virtual void ClearCacheObjectTypes(string typeName) { - lock (Locker) + using (new WriteLock(Locker)) { - var keysToRemove = DictionaryCache.Cast() - .Select(item => new DictionaryItemWrapper(item)) - .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName)) - .Select(c => c.Key) - .ToList(); + 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); - } } } - /// - /// Clears all objects in the System.Web.Cache with the System.Type specified - /// public virtual void ClearCacheObjectTypes() - { - lock (Locker) + { + using (new WriteLock(Locker)) { - var keysToRemove = DictionaryCache.Cast() - .Select(item => new DictionaryItemWrapper(item)) - .Where(c => DictionaryCache[c.Key.ToString()] != null && DictionaryCache[c.Key.ToString()].GetType() == typeof (T)) - .Select(c => c.Key) - .ToList(); + 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); - } - + 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); } } @@ -134,35 +162,27 @@ namespace Umbraco.Core.Cache } } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { 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.TryConvertTo() - into converted - where converted.Success - select converted.Result).ToList(); + select c.Value).ToList(); } /// /// Returns a cache item by key, does not update the cache if it isn't there. /// - /// /// /// - public virtual TT GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = DictionaryCache.Get(GetCacheKey(cacheKey)); - if (result == null) - { - return default(TT); - } - return result.TryConvertTo().Result; + return result; } - public abstract T GetCacheItem(string cacheKey, Func getCacheItem); + 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 diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 134b780f2b..26c6dc0af8 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -36,15 +36,12 @@ namespace Umbraco.Core.Cache } } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public override object GetCacheItem(string cacheKey, Func getCacheItem) { var ctx = _context(); var ck = GetCacheKey(cacheKey); - if (ctx.Items[ck] == null) - { - ctx.Items[ck] = getCacheItem(); - } - return (T)ctx.Items[ck]; + return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem()); } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index 02b4aa46d2..9838f8d1e6 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,4 +1,7 @@ using System; +using System.Collections; +using System.Linq; +using System.Threading; using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; @@ -13,7 +16,6 @@ namespace Umbraco.Core.Cache { private readonly System.Web.Caching.Cache _cache; private readonly DictionaryCacheWrapper _wrapper; - private static readonly object Locker = new object(); public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache) { @@ -22,6 +24,11 @@ namespace Umbraco.Core.Cache } protected override DictionaryCacheWrapper DictionaryCache + { + get { return _wrapper; } + } + + /// /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate /// public override void ClearCacheObjectTypes(Func predicate) @@ -48,207 +55,94 @@ namespace Umbraco.Core.Cache } } - /// - { - get { return _wrapper; } - } - /// /// Gets (and adds if necessary) an item from the cache with all of the default parameters /// - /// /// /// /// - public override T GetCacheItem(string cacheKey, Func getCacheItem) + 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 ab70cf63c0..e9bdde3ff2 100644 --- a/src/Umbraco.Core/Cache/ICacheProvider.cs +++ b/src/Umbraco.Core/Cache/ICacheProvider.cs @@ -6,19 +6,17 @@ 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); void ClearCacheObjectTypes(string typeName); void ClearCacheObjectTypes(); + void ClearCacheObjectTypes(Func predicate); void ClearCacheByKeySearch(string keyStartsWith); void ClearCacheByKeyExpression(string regexString); - IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); - T GetCacheItem(string cacheKey); - T GetCacheItem(string cacheKey, Func getCacheItem); + IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); + object GetCacheItem(string cacheKey); + object GetCacheItem(string cacheKey, Func getCacheItem); } } \ No newline at end of file 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 ff993bdbc0..ba88637661 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -23,11 +23,14 @@ namespace Umbraco.Core.Cache { } - public override void ClearCacheObjectTypes(Func predicate) + public virtual void ClearCacheObjectTypes(Func predicate) { } - public override void ClearCacheByKeySearch(string keyStartsWith) + + + + public virtual void ClearCacheByKeySearch(string keyStartsWith) { } @@ -35,55 +38,29 @@ namespace Umbraco.Core.Cache { } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - return Enumerable.Empty(); + return Enumerable.Empty(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { - return default(T); + return default(object); } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { 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 c9a37d830d..aa6a5bbb22 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(LockRecursionPolicy.SupportsRecursion); 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,31 +44,48 @@ 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(); + 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); - } } } public virtual void ClearCacheObjectTypes() { - using (new WriteLock(ClearLock)) + using (new WriteLock(Locker)) { - var keysToRemove = (from c in MemoryCache where c.Value.GetType() == typeof (T) select c.Key).ToList(); + 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); + } + } + + public virtual void ClearCacheObjectTypes(Func predicate) + { + 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); - } } } 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) @@ -80,7 +97,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) @@ -90,49 +107,34 @@ namespace Umbraco.Core.Cache } } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); + select c.Value).ToList(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = MemoryCache.Get(cacheKey); - if (result == null) - { - return default(T); - } - return result.TryConvertTo().Result; + return result; } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + 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) @@ -142,57 +144,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/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 67c0527393..cd58fc3dde 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -27,75 +27,47 @@ namespace Umbraco.Core.Cache public virtual void ClearCacheObjectTypes(string typeName) { - foreach (var key in StaticCache.Keys) - { - if (StaticCache[key] != null - && StaticCache[key].GetType().ToString().InvariantEquals(typeName)) - { - object val; - StaticCache.TryRemove(key, out val); - } - } + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName)); } public virtual void ClearCacheObjectTypes() { - foreach (var key in StaticCache.Keys) - { - if (StaticCache[key] != null - && StaticCache[key].GetType() == typeof(T)) - { - object val; - StaticCache.TryRemove(key, out val); - } - } + var typeOfT = typeof(T); + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT); + } + + public virtual void ClearCacheObjectTypes(Func predicate) + { + var typeOfT = typeof(T); + StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); } public virtual void ClearCacheByKeySearch(string keyStartsWith) { - foreach (var key in StaticCache.Keys) - { - if (key.InvariantStartsWith(keyStartsWith)) - { - ClearCacheItem(key); - } - } + StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)); } public virtual void ClearCacheByKeyExpression(string regexString) { - foreach (var key in StaticCache.Keys) - { - if (Regex.IsMatch(key, regexString)) - { - ClearCacheItem(key); - } - } + StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString)); } - public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return (from KeyValuePair c in StaticCache where c.Key.InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); + select c.Value).ToList(); } - public virtual T GetCacheItem(string cacheKey) + public virtual object GetCacheItem(string cacheKey) { var result = StaticCache[cacheKey]; - if (result == null) - { - return default(T); - } - return result.TryConvertTo().Result; + return result; } - public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual object GetCacheItem(string cacheKey, Func getCacheItem) { - return (T)StaticCache.GetOrAdd(cacheKey, getCacheItem()); + return StaticCache.GetOrAdd(cacheKey, key => getCacheItem()); } } diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 7ec76f165c..539ac796b7 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -12,357 +12,130 @@ using Umbraco.Core.Logging; namespace Umbraco.Core { - /// - /// Class that is exposed by the ApplicationContext for application wide caching purposes - /// + /// + /// Class that is exposed by the ApplicationContext for application wide caching purposes + /// public class CacheHelper - { - private readonly bool _enableCache; - private readonly ICacheProvider _requestCache; + { + private readonly bool _enableCache; + private readonly ICacheProvider _requestCache; private readonly ICacheProvider _nullRequestCache = new NullCacheProvider(); private readonly ICacheProvider _staticCache; private readonly ICacheProvider _nullStaticCache = new NullCacheProvider(); private readonly IRuntimeCacheProvider _httpCache; private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider(); - public CacheHelper(System.Web.Caching.Cache cache) - : this(cache, true) - { - } + /// + /// Creates a cache helper with disabled caches + /// + /// + /// + /// Good for unit testing + /// + public static CacheHelper CreateDisabledCacheHelper() + { + return new CacheHelper(null, null, null, false); + } - internal CacheHelper(System.Web.Caching.Cache cache, bool enableCache) - : this(new HttpRuntimeCacheProvider(cache), enableCache) - { - } - - internal CacheHelper(IRuntimeCacheProvider httpCacheProvider, bool enableCache) - : this(httpCacheProvider, new StaticCacheProvider(), new HttpRequestCacheProvider(HttpContext.Current), enableCache) + /// + /// Initializes a new instance for use in the web + /// + public CacheHelper() + : this( + new HttpRuntimeCacheProvider(HttpRuntime.Cache), + new StaticCacheProvider(), + new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current))) { } - internal CacheHelper( - IRuntimeCacheProvider httpCacheProvider, - ICacheProvider staticCacheProvider, - ICacheProvider requestCacheProvider, + /// + /// Initializes a new instance for use in the web + /// + /// + public CacheHelper(System.Web.Caching.Cache cache) + : this( + new HttpRuntimeCacheProvider(cache), + new StaticCacheProvider(), + new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current))) + { + } + + /// + /// Initializes a new instance based on the provided providers + /// + /// + /// + /// + public CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider) + : this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true) + { + } + + /// + /// Private ctor used for creating a disabled cache helper + /// + /// + /// + /// + /// + private CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider, bool enableCache) { - _httpCache = httpCacheProvider; - _staticCache = staticCacheProvider; + if (enableCache) + { + _httpCache = httpCacheProvider; + _staticCache = staticCacheProvider; + _requestCache = requestCacheProvider; + } + else + { + _httpCache = null; + _staticCache = null; + _requestCache = null; + } + _enableCache = enableCache; - _requestCache = requestCacheProvider; - } - - #region Request cache - - /// - /// Clears the item in umbraco's request cache - /// - internal void ClearAllRequestCache() - { - if (!_enableCache) - { - _nullRequestCache.ClearAllCache(); - } - else - { - _requestCache.ClearAllCache(); - } } /// - /// Clears the item in umbraco's request cache with the given key + /// Returns the current Request cache /// - /// Key - internal void ClearRequestCacheItem(string key) + public ICacheProvider RequestCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheItem(key); - } - else - { - _requestCache.ClearCacheItem(key); - } + get { return _enableCache ? _requestCache : _nullRequestCache; } } /// - /// 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) + public ICacheProvider StaticCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(typeName); - } - else - { - _requestCache.ClearCacheObjectTypes(typeName); - } + get { return _enableCache ? _staticCache : _nullStaticCache; } } /// - /// Clears all objects in the request cache with the System.Type specified + /// Returns the current Runtime cache /// - internal void ClearRequestCacheObjectTypes() + public IRuntimeCacheProvider RuntimeCache { - if (!_enableCache) - { - _nullRequestCache.ClearCacheObjectTypes(); - } - else - { - _requestCache.ClearCacheObjectTypes(); - } + get { return _enableCache ? _httpCache : _nullHttpCache; } } - /// - /// 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); - } - } + #region Legacy Runtime/Http Cache accessors - /// - /// 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); - } - } - - #endregion - - #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(); - } - } - - internal void ClearStaticCacheObjectTypes(Func predicate) - { - if (_enableCache) - _staticCache.ClearCacheObjectTypes(predicate); - else - _nullStaticCache.ClearCacheObjectTypes(predicate); - } - - /// - /// 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(); } @@ -376,9 +149,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); } @@ -394,9 +168,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); } @@ -409,9 +184,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(); } @@ -425,9 +201,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); } @@ -441,9 +218,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); } @@ -453,9 +231,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); } @@ -471,9 +250,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); } @@ -490,9 +270,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); } @@ -510,16 +291,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); } } @@ -532,17 +314,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); } } @@ -556,17 +339,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); } } @@ -581,6 +365,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, @@ -588,13 +373,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)); } } @@ -607,6 +398,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, @@ -614,11 +406,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)); } } @@ -633,13 +431,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); } } @@ -656,13 +454,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); } } @@ -675,19 +473,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)); } } @@ -701,6 +505,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, @@ -708,17 +513,22 @@ 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.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 91ee5db40f..0ed62f720a 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; @@ -38,6 +39,7 @@ namespace Umbraco.Core private bool _isComplete = false; private readonly UmbracoApplicationBase _umbracoApplication; protected ApplicationContext ApplicationContext { get; private set; } + protected CacheHelper ApplicationCache { get; set; } protected UmbracoApplicationBase UmbracoApplication { @@ -57,16 +59,19 @@ namespace Umbraco.Core InitializeProfilerResolver(); + CreateApplicationCache(); + _timer = DisposableTimer.DebugDuration("Umbraco application starting", "Umbraco application startup complete"); //create database and service contexts for the app context var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName); Database.Mapper = new PetaPocoMapper(); var dbContext = new DatabaseContext(dbFactory); - var serviceContext = new ServiceContext( - new PetaPocoUnitOfWorkProvider(dbFactory), - new FileUnitOfWorkProvider(), - new PublishingStrategy()); + var serviceContext = new ServiceContext( + new PetaPocoUnitOfWorkProvider(dbFactory), + new FileUnitOfWorkProvider(), + new PublishingStrategy(), + ApplicationCache); CreateApplicationContext(dbContext, serviceContext); @@ -94,7 +99,21 @@ namespace Umbraco.Core protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext) { //create the ApplicationContext - ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext); + ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext, ApplicationCache); + } + + /// + /// Creates and assigns the ApplicationCache based on a new instance of System.Web.Caching.Cache + /// + protected virtual void CreateApplicationCache() + { + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //we have no request based cache when not running in web-based context + new NullCacheProvider()); + + ApplicationCache = cacheHelper; } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 91532fdaad..eb53215896 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.Models.PublishedContent Logging.LogHelper.Debug("Clear all."); // ok and faster to do it by types, assuming noone else caches PublishedContentType instances //ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes(); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes(); } internal static void ClearContentType(int id) @@ -110,7 +110,7 @@ namespace Umbraco.Core.Models.PublishedContent Logging.LogHelper.Debug("Clear content type w/id {0}.", () => id); // requires a predicate because the key does not contain the ID // faster than key strings comparisons anyway - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes( + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( (key, value) => value.Id == id); } @@ -121,7 +121,7 @@ namespace Umbraco.Core.Models.PublishedContent // properties ie both its own properties and those that were inherited (it's based upon an // IContentTypeComposition) and so every PublishedContentType having a property based upon // the cleared data type, be it local or inherited, will be cleared. - ApplicationContext.Current.ApplicationCache.ClearStaticCacheObjectTypes( + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes( (key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id)); } @@ -130,7 +130,7 @@ namespace Umbraco.Core.Models.PublishedContent var key = string.Format("PublishedContentType_{0}_{1}", itemType == PublishedItemType.Content ? "content" : "media", alias.ToLowerInvariant()); - var type = ApplicationContext.Current.ApplicationCache.GetStaticCacheItem(key, + var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem(key, () => CreatePublishedContentType(itemType, alias)); return type; @@ -142,8 +142,8 @@ namespace Umbraco.Core.Models.PublishedContent return GetPublishedContentTypeCallback(alias); var contentType = itemType == PublishedItemType.Content - ? (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) - : (IContentTypeComposition) ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + ? (IContentTypeComposition)ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias) + : (IContentTypeComposition)ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); if (contentType == null) throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".", @@ -161,7 +161,7 @@ namespace Umbraco.Core.Models.PublishedContent { // see note above //ClearAll(); - ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_"); + ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_"); _getPublishedContentTypeCallBack = value; } diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index b6ccfc3d81..725697fa26 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -23,7 +23,10 @@ namespace Umbraco.Core.Services private Lazy _packagingService; private Lazy _serverRegistrationService; private Lazy _entityService; - private Lazy _relationService; + //private Lazy _relationService; + //private Lazy _treeService; + //private Lazy _sectionService; + //private Lazy _macroService; private Lazy _memberTypeService; /// @@ -38,9 +41,23 @@ namespace Umbraco.Core.Services /// /// /// - public ServiceContext(IContentService contentService, IMediaService mediaService, IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, PackagingService packagingService, IEntityService entityService, RelationService relationService) + /// + /// + /// + public ServiceContext( + IContentService contentService, + IMediaService mediaService, + IContentTypeService contentTypeService, + IDataTypeService dataTypeService, + IFileService fileService, + ILocalizationService localizationService, + PackagingService packagingService, + IEntityService entityService/*, + IRelationService relationService, + ISectionService sectionService, + IApplicationTreeService treeService*/) { - _contentService = new Lazy(() => contentService); + _contentService = new Lazy(() => contentService); _mediaService = new Lazy(() => mediaService); _contentTypeService = new Lazy(() => contentTypeService); _dataTypeService = new Lazy(() => dataTypeService); @@ -48,7 +65,9 @@ namespace Umbraco.Core.Services _localizationService = new Lazy(() => localizationService); _packagingService = new Lazy(() => packagingService); _entityService = new Lazy(() => entityService); - _relationService = new Lazy(() => relationService); + //_relationService = new Lazy(() => relationService); + //_sectionService = new Lazy(() => sectionService); + //_treeService = new Lazy(() => treeService); } /// @@ -57,11 +76,12 @@ namespace Umbraco.Core.Services /// /// /// - internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy) + /// + internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, CacheHelper cache) { - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, - //this needs to be lazy because when we create the service context it's generally before the - //resolvers have been initialized! + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, cache, + //this needs to be lazy because when we create the service context it's generally before the + //resolvers have been initialized! new Lazy(() => RepositoryResolver.Current.Factory)); } @@ -72,6 +92,7 @@ namespace Umbraco.Core.Services IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, + CacheHelper cache, Lazy repositoryFactory) { var provider = dbUnitOfWorkProvider; @@ -110,11 +131,21 @@ namespace Umbraco.Core.Services if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); - if (_relationService == null) - _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + //if (_relationService == null) + // _relationService = new Lazy(() => new RelationService(provider, repositoryFactory.Value, _entityService.Value)); + + //if (_treeService == null) + // _treeService = new Lazy(() => new ApplicationTreeService(cache)); + + //if (_sectionService == null) + // _sectionService = new Lazy(() => new SectionService(_userService.Value, _treeService.Value, cache)); + + //if (_macroService == null) + // _macroService = new Lazy(() => new MacroService(provider, repositoryFactory.Value)); if (_memberTypeService == null) _memberTypeService = new Lazy(() => new MemberTypeService(provider, repositoryFactory.Value)); + } /// @@ -124,22 +155,30 @@ namespace Umbraco.Core.Services { get { return _serverRegistrationService.Value; } } + + ///// + ///// Gets the + ///// + //public IMacroService MacroService + //{ + // get { return _macroService.Value; } + //} /// - /// Gets the + /// Gets the /// public IEntityService EntityService { get { return _entityService.Value; } } - /// - /// Gets the - /// - public RelationService RelationService - { - get { return _relationService.Value; } - } + ///// + ///// Gets the + ///// + //public IRelationService RelationService + //{ + // get { return _relationService.Value; } + //} /// /// Gets the @@ -212,7 +251,23 @@ namespace Umbraco.Core.Services { get { return _memberService.Value; } } - + + ///// + ///// Gets the + ///// + //public ISectionService SectionService + //{ + // get { return _sectionService.Value; } + //} + + ///// + ///// Gets the + ///// + //public IApplicationTreeService ApplicationTreeService + //{ + // get { return _treeService.Value; } + //} + /// /// Gets the MemberTypeService /// diff --git a/src/Umbraco.Core/Standalone/ServiceContextManager.cs b/src/Umbraco.Core/Standalone/ServiceContextManager.cs index 579bde0ff2..955c7a25df 100644 --- a/src/Umbraco.Core/Standalone/ServiceContextManager.cs +++ b/src/Umbraco.Core/Standalone/ServiceContextManager.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.UnitOfWork; @@ -45,13 +46,20 @@ namespace Umbraco.Core.Standalone { if (_serviceContext == null) { + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //we have no request based cache when running standalone + new NullCacheProvider()); + var dbFactory = new DefaultDatabaseFactory(_connectionString, _providerName); var dbContext = new DatabaseContext(dbFactory); Database.Mapper = new PetaPocoMapper(); _serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), - new PublishingStrategy()); + new PublishingStrategy(), + cacheHelper); //initialize the DatabaseContext dbContext.Initialize(_providerName); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 84a52c2947..8498e39e4d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -113,19 +113,20 @@ - + - - + + + @@ -725,7 +726,7 @@ - + diff --git a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs index 7fe009ffd7..9e4e6ccea7 100644 --- a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs +++ b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.BusinessLogic [SetUp] public void Initialize() { - ApplicationContext.Current = new ApplicationContext(false){IsReady = true}; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; InitializeDatabase(); InitializeApps(); InitializeAppConfigFile(); diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs index b7ed588bb8..5140995c38 100644 --- a/src/Umbraco.Tests/Cache/CacheProviderTests.cs +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -1,4 +1,5 @@ -using System.Web.UI; +using System.Linq; +using System.Web.UI; using NUnit.Framework; using Umbraco.Core.Cache; using umbraco; @@ -23,12 +24,69 @@ namespace Umbraco.Tests.Cache } [Test] - public void Can_Remove_By_Type_Name() + public void Can_Get_By_Search() { var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + var result = Provider.GetCacheItemsByKeySearch("Tes"); + + Assert.AreEqual(3, result.Count()); + } + + [Test] + public void Can_Clear_By_Expression() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("TTes1t", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheByKeyExpression("^\\w+es\\d.*"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_By_Search() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Tester2", () => cacheContent2); + Provider.GetCacheItem("Tes3", () => cacheContent3); + Provider.GetCacheItem("different4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheByKeySearch("Test"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_By_Key() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); Provider.GetCacheItem("Test1", () => cacheContent1); Provider.GetCacheItem("Test2", () => cacheContent2); Provider.GetCacheItem("Test3", () => cacheContent3); @@ -36,18 +94,76 @@ namespace Umbraco.Tests.Cache Assert.AreEqual(4, GetTotalItemCount); - Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + Provider.ClearCacheItem("Test1"); + Provider.ClearCacheItem("Test2"); + + Assert.AreEqual(2, GetTotalItemCount); + } + + [Test] + public void Can_Clear_All_Items() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearAllCache(); Assert.AreEqual(0, GetTotalItemCount); } + [Test] + public void Can_Add_When_Not_Available() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Assert.AreEqual(1, GetTotalItemCount); + } + + [Test] + public void Can_Get_When_Available() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var result = Provider.GetCacheItem("Test1", () => cacheContent1); + var result2 = Provider.GetCacheItem("Test1", () => cacheContent1); + Assert.AreEqual(1, GetTotalItemCount); + Assert.AreEqual(result, result2); + } + + [Test] + public void Can_Remove_By_Type_Name() + { + var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); + var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); + var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); + var cacheContent4 = new LiteralControl(); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + //Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + Provider.ClearCacheObjectTypes(typeof(MacroCacheContent).ToString()); + + Assert.AreEqual(1, GetTotalItemCount); + } + [Test] public void Can_Remove_By_Strong_Type() { var cacheContent1 = new MacroCacheContent(new LiteralControl(), "Test1"); var cacheContent2 = new MacroCacheContent(new LiteralControl(), "Test2"); var cacheContent3 = new MacroCacheContent(new LiteralControl(), "Test3"); - var cacheContent4 = new MacroCacheContent(new LiteralControl(), "Test4"); + var cacheContent4 = new LiteralControl(); Provider.GetCacheItem("Test1", () => cacheContent1); Provider.GetCacheItem("Test2", () => cacheContent2); Provider.GetCacheItem("Test3", () => cacheContent3); @@ -57,7 +173,7 @@ namespace Umbraco.Tests.Cache Provider.ClearCacheObjectTypes(); - Assert.AreEqual(0, GetTotalItemCount); + Assert.AreEqual(1, GetTotalItemCount); } } } \ No newline at end of file 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")); diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index c4f8977b35..0d822f05a9 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -5,6 +5,7 @@ using System.Web.UI; using System.Web.UI.WebControls; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Profiling; using umbraco; using umbraco.cms.businesslogic.macro; @@ -19,11 +20,15 @@ namespace Umbraco.Tests.Macros public void Setup() { //we DO want cache enabled for these tests - ApplicationContext.Current = new ApplicationContext(true); + var cacheHelper = new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + new NullCacheProvider()); + ApplicationContext.Current = new ApplicationContext(cacheHelper); ProfilerResolver.Current = new ProfilerResolver(new LogProfiler()) - { - CanResolveBeforeFrozen = true - }; + { + CanResolveBeforeFrozen = true + }; } [TearDown] diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index be31d2dbf4..126fad79d8 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests [Test] public void Can_Create_Empty_App_Context() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); Assert.Pass(); } @@ -43,11 +43,13 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), - new Mock().Object, + new Mock().Object/*, new RelationService( new Mock().Object, new RepositoryFactory(true), - new Mock().Object)); + new Mock().Object), + new Mock().Object, + new Mock().Object*/); Assert.Pass(); } @@ -79,19 +81,19 @@ namespace Umbraco.Tests new Mock().Object, new RepositoryFactory(true), new Mock().Object), - new Mock().Object, + new Mock().Object/*, new RelationService( new Mock().Object, new RepositoryFactory(true), - new Mock().Object)), - false); + new Mock().Object)*/), + CacheHelper.CreateDisabledCacheHelper()); Assert.Pass(); } - + [Test] public void Can_Assign_App_Context_Singleton() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); var result = ApplicationContext.EnsureContext(appCtx, true); Assert.AreEqual(appCtx, result); } @@ -99,8 +101,8 @@ namespace Umbraco.Tests [Test] public void Does_Not_Overwrite_App_Context_Singleton() { - ApplicationContext.EnsureContext(new ApplicationContext(false), true); - var appCtx = new ApplicationContext(false); + ApplicationContext.EnsureContext(new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()), true); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); var result = ApplicationContext.EnsureContext(appCtx, false); Assert.AreNotEqual(appCtx, result); } @@ -109,14 +111,14 @@ namespace Umbraco.Tests [Test] public void Can_Get_Umbraco_Context() { - var appCtx = new ApplicationContext(false); + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()); ApplicationContext.EnsureContext(appCtx, true); - + var umbCtx = UmbracoContext.EnsureContext( new Mock().Object, appCtx, true); - + Assert.AreEqual(umbCtx, UmbracoContext.Current); } diff --git a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs index 462936c26e..1d33949df2 100644 --- a/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Mvc/UmbracoViewPageTests.cs @@ -402,7 +402,7 @@ namespace Umbraco.Tests.Mvc //PublishedContentCache.UnitTesting = true; // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; - var appCtx = new ApplicationContext(false) { IsReady = true }; + var appCtx = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; var ctx = new UmbracoContext( GetHttpContextFactory(url, routeData).HttpContext, diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index f8f56d6dcb..bb0ac581bc 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -27,17 +27,19 @@ namespace Umbraco.Tests.Persistence RepositoryResolver.Current = new RepositoryResolver( new RepositoryFactory()); - + + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + ApplicationContext.Current = new ApplicationContext( //assign the db context new DatabaseContext(new DefaultDatabaseFactory()), //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; Resolution.Freeze(); } diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 25fe36cbf7..5bf2264c5c 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -15,18 +15,18 @@ namespace Umbraco.Tests.Persistence { private DatabaseContext _dbContext; - [SetUp] - public void Setup() - { - _dbContext = new DatabaseContext(new DefaultDatabaseFactory()); + [SetUp] + public void Setup() + { + _dbContext = new DatabaseContext(new DefaultDatabaseFactory()); - //unfortunately we have to set this up because the PetaPocoExtensions require singleton access - ApplicationContext.Current = new ApplicationContext(false) - { - DatabaseContext = _dbContext, - IsReady = true - }; - } + //unfortunately we have to set this up because the PetaPocoExtensions require singleton access + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) + { + DatabaseContext = _dbContext, + IsReady = true + }; + } [TearDown] public void TearDown() diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 18cf4bc278..6d5c9065d7 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent var caches = CreatePublishedContent(); - ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; var factory = new FakeHttpContextFactory("http://umbraco.local/"); StateHelper.HttpContext = factory.HttpContext; var context = new UmbracoContext( diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2806b1a931..610d135870 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent .Union(new[] { typeof(PublishedContentTests).Assembly }) }; - ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; + ApplicationContext.Current = new ApplicationContext(CacheHelper.CreateDisabledCacheHelper()) { IsReady = true }; // need to specify a custom callback for unit tests // AutoPublishedContentTypes generates properties automatically diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 1c7f2a39e6..eaa3c9c65c 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -24,27 +24,30 @@ namespace Umbraco.Tests.Services private PerThreadPetaPocoUnitOfWorkProvider _uowProvider; private PerThreadDatabaseFactory _dbFactory; - [SetUp] - public override void Initialize() - { - base.Initialize(); - - //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that - //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext - //threading environment, or a single apartment threading environment will not work for this test because - //it is multi-threaded. - _dbFactory = new PerThreadDatabaseFactory(); - //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory); + [SetUp] + public override void Initialize() + { + base.Initialize(); - //here we are going to override the ServiceContext because normally with our test cases we use a - //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. - //we need a new Database object for each thread. - _uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); - ApplicationContext.Services = new ServiceContext(_uowProvider, new FileUnitOfWorkProvider(), new PublishingStrategy()); + //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that + //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext + //threading environment, or a single apartment threading environment will not work for this test because + //it is multi-threaded. + _dbFactory = new PerThreadDatabaseFactory(); + //overwrite the local object + ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory); - CreateTestData(); - } + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + + //here we are going to override the ServiceContext because normally with our test cases we use a + //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. + //we need a new Database object for each thread. + _uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); + ApplicationContext.Services = new ServiceContext(_uowProvider, new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper); + + CreateTestData(); + } [TearDown] public override void TearDown() diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 755c6e5714..6bd46836ae 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -53,34 +53,38 @@ namespace Umbraco.Tests.TestHelpers public override void Initialize() { InitializeFirstRunFlags(); - + var path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + var dbFactory = new DefaultDatabaseFactory( GetDbConnectionString(), GetDbProviderName()); + _appContext = new ApplicationContext( - //assign the db context - new DatabaseContext(dbFactory), - //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + //assign the db context + new DatabaseContext(dbFactory), + //assign the service context + new ServiceContext(new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; base.Initialize(); - DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); + using (DisposableTimer.TraceDuration("init")) + { + DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString); + CreateSqlCeDatabase(); + InitializeDatabase(); - CreateSqlCeDatabase(); - - InitializeDatabase(); - - //ensure the configuration matches the current version for tests - SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); + //ensure the configuration matches the current version for tests + SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); + } } protected override void SetupApplicationContext() diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs index ffe068e5ce..3142394db4 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseTest.cs @@ -34,24 +34,24 @@ namespace Umbraco.Tests.TestHelpers string path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", path); - - try + + try + { + //Delete database file before continueing + string filePath = string.Concat(path, "\\test.sdf"); + if (File.Exists(filePath)) { - //Delete database file before continueing - string filePath = string.Concat(path, "\\test.sdf"); - if (File.Exists(filePath)) - { - File.Delete(filePath); - } + File.Delete(filePath); } - catch (Exception) - { - //if this doesn't work we have to make sure everything is reset! otherwise - // well run into issues because we've already set some things up - TearDown(); - throw; - } - + } + catch (Exception) + { + //if this doesn't work we have to make sure everything is reset! otherwise + // well run into issues because we've already set some things up + TearDown(); + throw; + } + RepositoryResolver.Current = new RepositoryResolver( new RepositoryFactory(true)); //disable all repo caches for tests! @@ -60,16 +60,19 @@ namespace Umbraco.Tests.TestHelpers new List { typeof(MySqlSyntaxProvider), typeof(SqlCeSyntaxProvider), typeof(SqlServerSyntaxProvider) }) { CanResolveBeforeFrozen = true }; Resolution.Freeze(); + + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + ApplicationContext.Current = new ApplicationContext( //assign the db context new DatabaseContext(new DefaultDatabaseFactory()), //assign the service context - new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy()), - //disable cache - false) - { - IsReady = true - }; + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; SqlSyntaxContext.SqlSyntaxProvider = SyntaxProvider; diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index a3e93bfc07..22d39d4559 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -2,6 +2,10 @@ using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Publishing; +using Umbraco.Core.Services; using Umbraco.Web; namespace Umbraco.Tests.TestHelpers @@ -71,8 +75,18 @@ namespace Umbraco.Tests.TestHelpers /// protected virtual void SetupApplicationContext() { - //DO NOT ENABLE CACHE - ApplicationContext.Current = new ApplicationContext(false) {IsReady = true}; + //disable cache + var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); + + ApplicationContext.Current = new ApplicationContext( + //assign the db context + new DatabaseContext(new DefaultDatabaseFactory()), + //assign the service context + new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper), + cacheHelper) + { + IsReady = true + }; } /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c04eb1a868..dbe4915ef1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -104,7 +104,7 @@ - + True ..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll @@ -146,17 +146,17 @@ - + + + + + + - - - - - diff --git a/src/Umbraco.Web/Standalone/ServiceContextManager.cs b/src/Umbraco.Web/Standalone/ServiceContextManager.cs index aa53b586ee..561fadfc2c 100644 --- a/src/Umbraco.Web/Standalone/ServiceContextManager.cs +++ b/src/Umbraco.Web/Standalone/ServiceContextManager.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.UnitOfWork; @@ -34,13 +35,23 @@ namespace Umbraco.Web.Standalone { if (_serviceContext == null) { + var cacheHelper = new CacheHelper( + //SD: Not sure if this is correct? Should we be using HttpRuntime.Cache here since this is for 'Web' ? + // just not quite sure what this standalone stuff is. + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + //SD: Not sure if this is correct? Should we be using request cache here since this is for 'Web' ? + // just not quite sure what this standalone stuff is. + new NullCacheProvider()); + var dbFactory = new DefaultDatabaseFactory(_connectionString, _providerName); var dbContext = new DatabaseContext(dbFactory); Database.Mapper = new PetaPocoMapper(); _serviceContext = new ServiceContext( new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), - new PublishingStrategy()); + new PublishingStrategy(), + cacheHelper); //initialize the DatabaseContext dbContext.Initialize(_providerName);