diff --git a/src/Umbraco.Core/Cache/CacheProviderBase.cs b/src/Umbraco.Core/Cache/CacheProviderBase.cs deleted file mode 100644 index 012d8b23b2..0000000000 --- a/src/Umbraco.Core/Cache/CacheProviderBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -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 abstract class CacheProviderBase - { - public abstract void ClearAllCache(); - public abstract void ClearCacheItem(string key); - public abstract void ClearCacheObjectTypes(string typeName); - public abstract void ClearCacheObjectTypes(); - public abstract void ClearCacheObjectTypes(Func predicate); - public abstract void ClearCacheByKeySearch(string keyStartsWith); - public abstract void ClearCacheByKeyExpression(string regexString); - public abstract IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); - public abstract T GetCacheItem(string cacheKey); - public abstract T GetCacheItem(string cacheKey, Func getCacheItem); - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs new file mode 100644 index 0000000000..adc02f9104 --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryCacheProdiverBase.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Cache +{ + internal abstract class DictionaryCacheProdiverBase : ICacheProvider + { + private static readonly object Locker = new object(); + protected abstract DictionaryCacheWrapper DictionaryCache { get; } + + /// + /// Clears everything in umbraco's runtime cache + /// + /// + /// Does not clear other stuff the user has put in httpruntime.cache! + /// + public virtual void ClearAllCache() + { + lock (Locker) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + } + + /// + /// Clears the item in umbraco's runtime cache with the given key + /// + /// Key + public virtual void ClearCacheItem(string key) + { + lock (Locker) + { + if (DictionaryCache[GetCacheKey(key)] == null) return; + DictionaryCache.Remove(GetCacheKey(key)); ; + } + } + + /// + /// Clears all objects in the System.Web.Cache with the System.Type name as the + /// input parameter. (using [object].GetType()) + /// + /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" + public virtual void ClearCacheObjectTypes(string typeName) + { + lock (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(); + + 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) + { + 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(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + + } + } + + /// + /// Clears all cache items that starts with the key passed. + /// + /// The start of the key + public virtual void ClearCacheByKeySearch(string keyStartsWith) + { + var keysToRemove = DictionaryCache.Cast() + .Select(item => new DictionaryItemWrapper(item)) + .Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))) + .Select(c => c.Key) + .ToList(); + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + + /// + /// Clears all cache items that have a key that matches the regular expression + /// + /// + public virtual void ClearCacheByKeyExpression(string regexString) + { + var keysToRemove = new List(); + foreach (var item in DictionaryCache) + { + var c = new DictionaryItemWrapper(item); + var s = c.Key as string; + if (s != null) + { + var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix)); + if (Regex.IsMatch(withoutPrefix, regexString)) + { + keysToRemove.Add(c.Key); + } + } + } + + foreach (var k in keysToRemove) + { + DictionaryCache.Remove(k); + } + } + + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + { + 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(); + } + + /// + /// Returns a cache item by key, does not update the cache if it isn't there. + /// + /// + /// + /// + public virtual TT GetCacheItem(string cacheKey) + { + var result = DictionaryCache.Get(GetCacheKey(cacheKey)); + if (result == null) + { + return default(TT); + } + return result.TryConvertTo().Result; + } + + public abstract T GetCacheItem(string cacheKey, Func getCacheItem); + + /// + /// We prefix all cache keys with this so that we know which ones this class has created when + /// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create. + /// + protected const string CacheItemPrefix = "umbrtmche"; + + protected string GetCacheKey(string key) + { + return string.Format("{0}-{1}", CacheItemPrefix, key); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs b/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs new file mode 100644 index 0000000000..8203b6e481 --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryCacheWrapper.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; + +namespace Umbraco.Core.Cache +{ + internal class DictionaryCacheWrapper : IEnumerable + { + private readonly IEnumerable _inner; + private readonly Func _get; + private readonly Action _remove; + + public DictionaryCacheWrapper( + IEnumerable inner, + Func get, + Action remove) + { + _inner = inner; + _get = get; + _remove = remove; + } + + public object this[object key] + { + get + { + return Get(key); + } + } + + public object Get(object key) + { + return _get(key); + } + + public void Remove(object key) + { + _remove(key); + } + + public IEnumerator GetEnumerator() + { + return _inner.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs b/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs new file mode 100644 index 0000000000..4a1ecaf9ec --- /dev/null +++ b/src/Umbraco.Core/Cache/DictionaryItemWrapper.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Core.Cache +{ + internal class DictionaryItemWrapper + { + public DictionaryItemWrapper(dynamic item) + { + Key = item.Key; + Value = item.Value; + } + + public object Key { get; private set; } + public object Value { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs new file mode 100644 index 0000000000..134b780f2b --- /dev/null +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache provider that caches items in the HttpContext.Items + /// + internal class HttpRequestCacheProvider : DictionaryCacheProdiverBase + { + private readonly Func _context; + + public HttpRequestCacheProvider(HttpContext context) + { + _context = () => new HttpContextWrapper(context); + } + + public HttpRequestCacheProvider(Func context) + { + _context = context; + } + + protected override DictionaryCacheWrapper DictionaryCache + { + get + { + var ctx = _context(); + return new DictionaryCacheWrapper( + ctx.Items, + o => ctx.Items[o], + o => ctx.Items.Remove(o)); + } + } + + public override T 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]; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index f5fc0e237f..02b4aa46d2 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -1,109 +1,27 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +using System.Web; using System.Web.Caching; using Umbraco.Core.Logging; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; namespace Umbraco.Core.Cache { /// /// A CacheProvider that wraps the logic of the HttpRuntime.Cache /// - internal class HttpRuntimeCacheProvider : RuntimeCacheProviderBase + internal class HttpRuntimeCacheProvider : DictionaryCacheProdiverBase, IRuntimeCacheProvider { 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) { _cache = cache; + _wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString())); } - /// - /// Clears everything in umbraco's runtime cache, which means that not only - /// umbraco content is removed, but also other cache items from pages running in - /// the same application / website. Use with care :-) - /// - public override void ClearAllCache() - { - var cacheEnumerator = _cache.GetEnumerator(); - while (cacheEnumerator.MoveNext()) - { - _cache.Remove(cacheEnumerator.Key.ToString()); - } - } - - /// - /// Clears the item in umbraco's runtime cache with the given key - /// - /// Key - public override void ClearCacheItem(string key) - { - // NH 10 jan 2012 - // Patch by the always wonderful Stéphane Gay to avoid cache null refs - lock (Locker) - { - if (_cache[key] == null) return; - _cache.Remove(key); ; - } - } - - - /// - /// Clears all objects in the System.Web.Cache with the System.Type name as the - /// input parameter. (using [object].GetType()) - /// - /// The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument" - public override void ClearCacheObjectTypes(string typeName) - { - try - { - lock (Locker) - { - foreach (DictionaryEntry c in _cache) - { - if (_cache[c.Key.ToString()] != null - && _cache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName)) - { - _cache.Remove(c.Key.ToString()); - } - } - } - } - catch (Exception e) - { - LogHelper.Error("Cache clearing error", e); - } - } - - /// - /// Clears all objects in the System.Web.Cache with the System.Type specified - /// - public override void ClearCacheObjectTypes() - { - try - { - lock (Locker) - { - foreach (DictionaryEntry c in _cache) - { - if (_cache[c.Key.ToString()] != null - && _cache[c.Key.ToString()].GetType() == typeof(T)) - { - _cache.Remove(c.Key.ToString()); - } - } - } - } - catch (Exception e) - { - LogHelper.Error("Cache clearing error", e); - } - } - - /// + protected override DictionaryCacheWrapper DictionaryCache /// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate /// public override void ClearCacheObjectTypes(Func predicate) @@ -131,69 +49,18 @@ namespace Umbraco.Core.Cache } /// - /// Clears all cache items that starts with the key passed. - /// - /// The start of the key - public override void ClearCacheByKeySearch(string keyStartsWith) { - foreach (DictionaryEntry c in _cache) - { - if (c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith)) - { - ClearCacheItem((string)c.Key); - } - } - } - - /// - /// Clears all cache items that have a key that matches the regular expression - /// - /// - public override void ClearCacheByKeyExpression(string regexString) - { - foreach (DictionaryEntry c in _cache) - { - if (c.Key is string && Regex.IsMatch(((string)c.Key), regexString)) - { - ClearCacheItem((string)c.Key); - } - } - } - - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) - { - return (from DictionaryEntry c in _cache - where c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith) - select c.Value.TryConvertTo() - into attempt - where attempt.Success - select attempt.Result).ToList(); - } - - /// - /// Returns a cache item by key, does not update the cache if it isn't there. - /// - /// - /// - /// - public override TT GetCacheItem(string cacheKey) - { - var result = _cache.Get(cacheKey); - if (result == null) - { - return default(TT); - } - return result.TryConvertTo().Result; + get { return _wrapper; } } /// /// Gets (and adds if necessary) an item from the cache with all of the default parameters /// - /// + /// /// /// /// - public override TT GetCacheItem(string cacheKey, Func getCacheItem) + public override T GetCacheItem(string cacheKey, Func getCacheItem) { return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem, Locker); } @@ -206,7 +73,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) { return GetCacheItem(cacheKey, null, timeout, getCacheItem); @@ -221,7 +88,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { @@ -238,7 +105,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { @@ -256,7 +123,7 @@ namespace Umbraco.Core.Cache /// This will set an absolute expiration from now until the timeout /// /// - public override TT GetCacheItem(string cacheKey, + public virtual TT GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, @@ -282,12 +149,14 @@ namespace Umbraco.Core.Cache CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem, object syncLock) { - var result = _cache.Get(cacheKey); + cacheKey = GetCacheKey(cacheKey); + + var result = DictionaryCache.Get(cacheKey); if (result == null) { lock (syncLock) { - result = _cache.Get(cacheKey); + result = DictionaryCache.Get(cacheKey); if (result == null) { result = getCacheItem(); @@ -311,7 +180,7 @@ namespace Umbraco.Core.Cache /// /// /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) { @@ -326,7 +195,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) @@ -343,7 +212,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, @@ -362,7 +231,7 @@ namespace Umbraco.Core.Cache /// /// This will set an absolute expiration from now until the timeout /// - public override void InsertCacheItem(string cacheKey, + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, @@ -372,6 +241,8 @@ namespace Umbraco.Core.Cache object result = getCacheItem(); if (result != null) { + 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), diff --git a/src/Umbraco.Core/Cache/ICacheProvider.cs b/src/Umbraco.Core/Cache/ICacheProvider.cs new file mode 100644 index 0000000000..ab70cf63c0 --- /dev/null +++ b/src/Umbraco.Core/Cache/ICacheProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +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 + { + void ClearAllCache(); + void ClearCacheItem(string key); + void ClearCacheObjectTypes(string typeName); + void ClearCacheObjectTypes(); + void ClearCacheByKeySearch(string keyStartsWith); + void ClearCacheByKeyExpression(string regexString); + IEnumerable GetCacheItemsByKeySearch(string keyStartsWith); + T GetCacheItem(string cacheKey); + T 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 new file mode 100644 index 0000000000..8dd1d31af0 --- /dev/null +++ b/src/Umbraco.Core/Cache/IRuntimeCacheProvider.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; +using System.Web.Caching; + +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 + { + 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); + } +} diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs index 0ffecf59e3..ff993bdbc0 100644 --- a/src/Umbraco.Core/Cache/NullCacheProvider.cs +++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs @@ -5,21 +5,21 @@ using System.Web.Caching; namespace Umbraco.Core.Cache { - internal class NullCacheProvider : RuntimeCacheProviderBase + internal class NullCacheProvider : IRuntimeCacheProvider { - public override void ClearAllCache() + public virtual void ClearAllCache() { } - public override void ClearCacheItem(string key) + public virtual void ClearCacheItem(string key) { } - public override void ClearCacheObjectTypes(string typeName) + public virtual void ClearCacheObjectTypes(string typeName) { } - public override void ClearCacheObjectTypes() + public virtual void ClearCacheObjectTypes() { } @@ -31,58 +31,58 @@ namespace Umbraco.Core.Cache { } - public override void ClearCacheByKeyExpression(string regexString) + public virtual void ClearCacheByKeyExpression(string regexString) { } - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { return Enumerable.Empty(); } - public override T GetCacheItem(string cacheKey) + public virtual T GetCacheItem(string cacheKey) { return default(T); } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { return getCacheItem(); } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { } - public override void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) + public virtual void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem) { } } diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs new file mode 100644 index 0000000000..c9a37d830d --- /dev/null +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.Caching; +using System.Text.RegularExpressions; +using System.Threading; +using System.Web.Caching; +using Umbraco.Core.Logging; +using CacheItemPriority = System.Web.Caching.CacheItemPriority; + +namespace Umbraco.Core.Cache +{ + /// + /// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache + /// + internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider + { + private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim(); + internal ObjectCache MemoryCache; + + public ObjectCacheRuntimeCacheProvider() + { + MemoryCache = new MemoryCache("in-memory"); + } + + public virtual void ClearAllCache() + { + using (new WriteLock(ClearLock)) + { + MemoryCache.DisposeIfDisposable(); + MemoryCache = new MemoryCache("in-memory"); + } + } + + public virtual void ClearCacheItem(string key) + { + using (new WriteLock(ClearLock)) + { + if (MemoryCache[key] == null) return; + MemoryCache.Remove(key); + } + } + + public virtual void ClearCacheObjectTypes(string typeName) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Value.GetType().ToString().InvariantEquals(typeName) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheObjectTypes() + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Value.GetType() == typeof (T) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheByKeySearch(string keyStartsWith) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + public virtual void ClearCacheByKeyExpression(string regexString) + { + using (new WriteLock(ClearLock)) + { + var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList(); + foreach (var k in keysToRemove) + { + MemoryCache.Remove(k); + } + } + } + + 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(); + } + + public virtual T GetCacheItem(string cacheKey) + { + var result = MemoryCache.Get(cacheKey); + if (result == null) + { + return default(T); + } + return result.TryConvertTo().Result; + } + + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) + { + return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem); + } + + public virtual T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem) + { + 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)) + { + var result = MemoryCache.Get(cacheKey); + if (result == null) + { + lck.UpgradeToWriteLock(); + + 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.)); + + MemoryCache.Set(cacheKey, result, policy); + } + } + return result.TryConvertTo().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) + { + 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.)); + + MemoryCache.Set(cacheKey, result, policy); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs deleted file mode 100644 index d88a3922bb..0000000000 --- a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Text; -using System.Web.Caching; - -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 abstract class RuntimeCacheProviderBase : CacheProviderBase - { - public abstract T GetCacheItem(string cacheKey, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func getCacheItem); - public abstract T GetCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - public abstract void InsertCacheItem(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func getCacheItem); - } -} diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs index 765e84fd7c..67c0527393 100644 --- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs +++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs @@ -10,51 +10,72 @@ namespace Umbraco.Core.Cache /// /// A cache provider that statically caches everything in an in memory dictionary /// - internal class StaticCacheProvider : CacheProviderBase + internal class StaticCacheProvider : ICacheProvider { - private readonly ConcurrentDictionary _staticCache = new ConcurrentDictionary(); + internal readonly ConcurrentDictionary StaticCache = new ConcurrentDictionary(); - public override void ClearAllCache() + public virtual void ClearAllCache() { - _staticCache.Clear(); + StaticCache.Clear(); } - public override void ClearCacheItem(string key) + public virtual void ClearCacheItem(string key) { object val; - _staticCache.TryRemove(key, out val); + StaticCache.TryRemove(key, out val); } - public override void ClearCacheObjectTypes(string typeName) + public virtual void ClearCacheObjectTypes(string typeName) { - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName)); + foreach (var key in StaticCache.Keys) + { + if (StaticCache[key] != null + && StaticCache[key].GetType().ToString().InvariantEquals(typeName)) + { + object val; + StaticCache.TryRemove(key, out val); + } + } } - public override void ClearCacheObjectTypes() + public virtual void ClearCacheObjectTypes() { - var typeOfT = typeof (T); - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT); + foreach (var key in StaticCache.Keys) + { + if (StaticCache[key] != null + && StaticCache[key].GetType() == typeof(T)) + { + object val; + StaticCache.TryRemove(key, out val); + } + } } - public override void ClearCacheObjectTypes(Func predicate) + public virtual void ClearCacheByKeySearch(string keyStartsWith) { - var typeOfT = typeof(T); - _staticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value)); + foreach (var key in StaticCache.Keys) + { + if (key.InvariantStartsWith(keyStartsWith)) + { + ClearCacheItem(key); + } + } } - public override void ClearCacheByKeySearch(string keyStartsWith) + public virtual void ClearCacheByKeyExpression(string regexString) { - _staticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)); + foreach (var key in StaticCache.Keys) + { + if (Regex.IsMatch(key, regexString)) + { + ClearCacheItem(key); + } + } } - public override void ClearCacheByKeyExpression(string regexString) + public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { - _staticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString)); - } - - public override IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) - { - return (from KeyValuePair c in _staticCache + return (from KeyValuePair c in StaticCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Value.TryConvertTo() into attempt @@ -62,9 +83,9 @@ namespace Umbraco.Core.Cache select attempt.Result).ToList(); } - public override T GetCacheItem(string cacheKey) + public virtual T GetCacheItem(string cacheKey) { - var result = _staticCache[cacheKey]; + var result = StaticCache[cacheKey]; if (result == null) { return default(T); @@ -72,9 +93,9 @@ namespace Umbraco.Core.Cache return result.TryConvertTo().Result; } - public override T GetCacheItem(string cacheKey, Func getCacheItem) + public virtual T GetCacheItem(string cacheKey, Func getCacheItem) { - return (T)_staticCache.GetOrAdd(cacheKey, key => getCacheItem()); + return (T)StaticCache.GetOrAdd(cacheKey, getCacheItem()); } } diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 8f7edeb777..7ec76f165c 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -18,10 +18,12 @@ namespace Umbraco.Core public class CacheHelper { private readonly bool _enableCache; - private readonly CacheProviderBase _staticCache; - private readonly CacheProviderBase _nullStaticCache = new NullCacheProvider(); - private readonly RuntimeCacheProviderBase _httpCache; - private readonly RuntimeCacheProviderBase _nullHttpCache = new NullCacheProvider(); + 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) @@ -33,18 +35,171 @@ namespace Umbraco.Core { } - internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, bool enableCache) - : this(httpCacheProvider, new StaticCacheProvider(), enableCache) + internal CacheHelper(IRuntimeCacheProvider httpCacheProvider, bool enableCache) + : this(httpCacheProvider, new StaticCacheProvider(), new HttpRequestCacheProvider(HttpContext.Current), enableCache) { } - internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, CacheProviderBase staticCacheProvider, bool enableCache) + internal CacheHelper( + IRuntimeCacheProvider httpCacheProvider, + ICacheProvider staticCacheProvider, + ICacheProvider requestCacheProvider, + bool enableCache) { _httpCache = httpCacheProvider; _staticCache = staticCacheProvider; _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 + /// + /// Key + internal void ClearRequestCacheItem(string key) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheItem(key); + } + else + { + _requestCache.ClearCacheItem(key); + } + } + + /// + /// Clears all objects in the request 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 ClearRequestCacheObjectTypes(string typeName) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheObjectTypes(typeName); + } + else + { + _requestCache.ClearCacheObjectTypes(typeName); + } + } + + /// + /// Clears all objects in the request cache with the System.Type specified + /// + internal void ClearRequestCacheObjectTypes() + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheObjectTypes(); + } + else + { + _requestCache.ClearCacheObjectTypes(); + } + } + + /// + /// Clears all request cache items that starts with the key passed. + /// + /// The start of the key + internal void ClearRequestCacheByKeySearch(string keyStartsWith) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheByKeySearch(keyStartsWith); + } + else + { + _requestCache.ClearCacheByKeySearch(keyStartsWith); + } + } + + /// + /// Clears all cache items that have a key that matches the regular expression + /// + /// + internal void ClearRequestCacheByKeyExpression(string regexString) + { + if (!_enableCache) + { + _nullRequestCache.ClearCacheByKeyExpression(regexString); + } + else + { + _requestCache.ClearCacheByKeyExpression(regexString); + } + } + + internal IEnumerable GetRequestCacheItemsByKeySearch(string keyStartsWith) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItemsByKeySearch(keyStartsWith); + } + else + { + return _requestCache.GetCacheItemsByKeySearch(keyStartsWith); + } + } + + /// + /// Returns a request cache item by key, does not update the cache if it isn't there. + /// + /// + /// + /// + internal TT GetRequestCacheItem(string cacheKey) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItem(cacheKey); + } + else + { + return _requestCache.GetCacheItem(cacheKey); + } + } + + /// + /// Gets (and adds if necessary) an item from the request cache with all of the default parameters + /// + /// + /// + /// + /// + internal TT GetRequestCacheItem(string cacheKey, Func getCacheItem) + { + if (!_enableCache) + { + return _nullRequestCache.GetCacheItem(cacheKey, getCacheItem); + } + else + { + return _requestCache.GetCacheItem(cacheKey, getCacheItem); + } + } + + #endregion + #region Static cache /// @@ -200,8 +355,7 @@ namespace Umbraco.Core } #endregion - - + #region Runtime/Http Cache /// /// Clears the item in umbraco's runtime cache diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 98e9c17650..84a52c2947 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -113,10 +113,15 @@ - + - + + + + + + diff --git a/src/Umbraco.Tests/Cache/CacheHelperTests.cs b/src/Umbraco.Tests/Cache/CacheHelperTests.cs deleted file mode 100644 index afe296dd8b..0000000000 --- a/src/Umbraco.Tests/Cache/CacheHelperTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Web; -using System.Web.Caching; -using System.Web.UI; -using NUnit.Framework; -using Umbraco.Core; -using umbraco; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class CacheHelperTests - { - - private CacheHelper _helper; - - [SetUp] - public void Setup() - { - _helper = new CacheHelper(HttpRuntime.Cache); - } - - [TearDown] - public void TearDown() - { - _helper.ClearAllCache(); - } - - [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 MacroCacheContent(new LiteralControl(), "Test4"); - _helper.InsertCacheItem("Test1", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent1); - _helper.InsertCacheItem("Test2", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent2); - _helper.InsertCacheItem("Test3", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent3); - _helper.InsertCacheItem("Test4", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent4); - - Assert.AreEqual(4, HttpRuntime.Cache.Count); - - _helper.ClearCacheObjectTypes("umbraco.MacroCacheContent"); - - Assert.AreEqual(0, HttpRuntime.Cache.Count); - } - - [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"); - _helper.InsertCacheItem("Test1", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent1); - _helper.InsertCacheItem("Test2", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent2); - _helper.InsertCacheItem("Test3", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent3); - _helper.InsertCacheItem("Test4", CacheItemPriority.Default, new TimeSpan(0, 0, 60), () => cacheContent4); - - Assert.AreEqual(4, HttpRuntime.Cache.Count); - - _helper.ClearCacheObjectTypes(); - - Assert.AreEqual(0, HttpRuntime.Cache.Count); - } - - [Test] - public void Can_Add_Remove_Struct_Strongly_Typed_With_Null() - { - var now = DateTime.Now; - _helper.InsertCacheItem("DateTimeTest", CacheItemPriority.Default, new TimeSpan(0, 0, 0, 0, 200), () => now); - Assert.AreEqual(now, _helper.GetCacheItem("DateTimeTest")); - Assert.AreEqual(now, _helper.GetCacheItem("DateTimeTest")); - - Thread.Sleep(300); //sleep longer than the cache expiration - - Assert.AreEqual(default(DateTime), _helper.GetCacheItem("DateTimeTest")); - Assert.AreEqual(null, _helper.GetCacheItem("DateTimeTest")); - } - - } -} diff --git a/src/Umbraco.Tests/Cache/CacheProviderTests.cs b/src/Umbraco.Tests/Cache/CacheProviderTests.cs new file mode 100644 index 0000000000..b7ed588bb8 --- /dev/null +++ b/src/Umbraco.Tests/Cache/CacheProviderTests.cs @@ -0,0 +1,63 @@ +using System.Web.UI; +using NUnit.Framework; +using Umbraco.Core.Cache; +using umbraco; + +namespace Umbraco.Tests.Cache +{ + public abstract class CacheProviderTests + { + internal abstract ICacheProvider Provider { get; } + protected abstract int GetTotalItemCount { get; } + + [SetUp] + public virtual void Setup() + { + + } + + [TearDown] + public virtual void TearDown() + { + Provider.ClearAllCache(); + } + + [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 MacroCacheContent(new LiteralControl(), "Test4"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheObjectTypes("umbraco.MacroCacheContent"); + + Assert.AreEqual(0, 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"); + Provider.GetCacheItem("Test1", () => cacheContent1); + Provider.GetCacheItem("Test2", () => cacheContent2); + Provider.GetCacheItem("Test3", () => cacheContent3); + Provider.GetCacheItem("Test4", () => cacheContent4); + + Assert.AreEqual(4, GetTotalItemCount); + + Provider.ClearCacheObjectTypes(); + + Assert.AreEqual(0, GetTotalItemCount); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs b/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs new file mode 100644 index 0000000000..dcbddbb44e --- /dev/null +++ b/src/Umbraco.Tests/Cache/HttpRequestCacheProviderTests.cs @@ -0,0 +1,52 @@ +using NUnit.Framework; +using Umbraco.Core.Cache; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class HttpRequestCacheProviderTests : CacheProviderTests + { + private HttpRequestCacheProvider _provider; + private FakeHttpContextFactory _ctx; + + public override void Setup() + { + base.Setup(); + _ctx = new FakeHttpContextFactory("http://localhost/test"); + _provider = new HttpRequestCacheProvider(() => _ctx.HttpContext); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + protected override int GetTotalItemCount + { + get { return _ctx.HttpContext.Items.Count; } + } + } + + [TestFixture] + public class StaticCacheProviderTests : CacheProviderTests + { + private StaticCacheProvider _provider; + + public override void Setup() + { + base.Setup(); + _provider = new StaticCacheProvider(); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + protected override int GetTotalItemCount + { + get { return _provider.StaticCache.Count; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs new file mode 100644 index 0000000000..9cbbc926f8 --- /dev/null +++ b/src/Umbraco.Tests/Cache/HttpRuntimeCacheProviderTests.cs @@ -0,0 +1,33 @@ +using System.Web; +using NUnit.Framework; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class HttpRuntimeCacheProviderTests : RuntimeCacheProviderTests + { + private HttpRuntimeCacheProvider _provider; + + protected override int GetTotalItemCount + { + get { return HttpRuntime.Cache.Count; } + } + + public override void Setup() + { + base.Setup(); + _provider = new HttpRuntimeCacheProvider(HttpRuntime.Cache); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + internal override IRuntimeCacheProvider RuntimeProvider + { + get { return _provider; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs b/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs new file mode 100644 index 0000000000..e373fdda4d --- /dev/null +++ b/src/Umbraco.Tests/Cache/ObjectCacheProviderTests.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + [TestFixture] + public class ObjectCacheProviderTests : RuntimeCacheProviderTests + { + private ObjectCacheRuntimeCacheProvider _provider; + + protected override int GetTotalItemCount + { + get { return _provider.MemoryCache.Count(); } + } + + public override void Setup() + { + base.Setup(); + _provider = new ObjectCacheRuntimeCacheProvider(); + } + + internal override ICacheProvider Provider + { + get { return _provider; } + } + + internal override IRuntimeCacheProvider RuntimeProvider + { + get { return _provider; } + } + } +} diff --git a/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs new file mode 100644 index 0000000000..03ad355f42 --- /dev/null +++ b/src/Umbraco.Tests/Cache/RuntimeCacheProviderTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Web.Caching; +using NUnit.Framework; +using Umbraco.Core.Cache; + +namespace Umbraco.Tests.Cache +{ + public abstract class RuntimeCacheProviderTests : CacheProviderTests + { + + internal abstract IRuntimeCacheProvider RuntimeProvider { get; } + + + [Test] + 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); + Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); + Assert.AreEqual(now, Provider.GetCacheItem("DateTimeTest")); + + Thread.Sleep(300); //sleep longer than the cache expiration + + Assert.AreEqual(default(DateTime), Provider.GetCacheItem("DateTimeTest")); + Assert.AreEqual(null, Provider.GetCacheItem("DateTimeTest")); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f36ac5008a..c04eb1a868 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -101,6 +101,7 @@ ..\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll + @@ -151,7 +152,11 @@ - + + + + +