diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
index e79b61ed5d..6a53e8bc5c 100644
--- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
+++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs
@@ -1,9 +1,11 @@
using System;
+using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Threading;
+using System.Web;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.Caching
@@ -11,6 +13,19 @@ namespace Umbraco.Core.Persistence.Caching
///
/// The Runtime Cache provider looks up objects in the Runtime cache for fast retrival
///
+ ///
+ ///
+ /// If a web session is detected then the HttpRuntime.Cache will be used for the runtime cache, otherwise a custom
+ /// MemoryCache instance will be used. It is important to use the HttpRuntime.Cache when a web session is detected so
+ /// that the memory management of cache in IIS can be handled appopriately.
+ ///
+ /// When a web sessions is detected we will pre-fix all HttpRuntime.Cache entries so that when we clear it we are only
+ /// clearing items that have been inserted by this provider.
+ ///
+ /// NOTE: These changes are all temporary until we finalize the ApplicationCache implementation which will support static cache, runtime cache
+ /// and request based cache which will all live in one central location so it is easily managed.
+ ///
+ ///
internal sealed class RuntimeCacheProvider : IRepositoryCacheProvider
{
#region Singleton
@@ -21,19 +36,25 @@ namespace Umbraco.Core.Persistence.Caching
private RuntimeCacheProvider()
{
+ if (HttpContext.Current == null)
+ {
+ _memoryCache = new MemoryCache("in-memory");
+ }
}
#endregion
//TODO Save this in cache as well, so its not limited to a single server usage
private readonly ConcurrentHashSet _keyTracker = new ConcurrentHashSet();
- private ObjectCache _memoryCache = new MemoryCache("in-memory");
+ private ObjectCache _memoryCache;
private static readonly ReaderWriterLockSlim ClearLock = new ReaderWriterLockSlim();
public IEntity GetById(Type type, Guid id)
{
var key = GetCompositeId(type, id);
- var item = _memoryCache.Get(key);
+ var item = HttpContext.Current == null
+ ? _memoryCache.Get(key)
+ : HttpRuntime.Cache.Get(key);
return item as IEntity;
}
@@ -41,7 +62,11 @@ namespace Umbraco.Core.Persistence.Caching
{
foreach (var guid in ids)
{
- yield return _memoryCache.Get(GetCompositeId(type, guid)) as IEntity;
+ var item = HttpContext.Current == null
+ ? _memoryCache.Get(GetCompositeId(type, guid))
+ : HttpRuntime.Cache.Get(GetCompositeId(type, guid));
+
+ yield return item as IEntity;
}
}
@@ -51,7 +76,11 @@ namespace Umbraco.Core.Persistence.Caching
{
if (key.StartsWith(type.Name))
{
- yield return _memoryCache.Get(key) as IEntity;
+ var item = HttpContext.Current == null
+ ? _memoryCache.Get(key)
+ : HttpRuntime.Cache.Get(key);
+
+ yield return item as IEntity;
}
}
}
@@ -59,21 +88,34 @@ namespace Umbraco.Core.Persistence.Caching
public void Save(Type type, IEntity entity)
{
var key = GetCompositeId(type, entity.Id);
- var exists = _memoryCache.GetCacheItem(key) != null;
+
_keyTracker.TryAdd(key);
- if (exists)
+
+ //NOTE: Before we were checking if it already exists but the MemoryCache.Set handles this implicitly and does
+ // an add or update, same goes for HttpRuntime.Cache.Insert.
+
+ if (HttpContext.Current == null)
{
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
- return;
}
-
- _memoryCache.Add(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
+ else
+ {
+ HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
+ }
}
public void Delete(Type type, IEntity entity)
{
var key = GetCompositeId(type, entity.Id);
- _memoryCache.Remove(key);
+ if (HttpContext.Current == null)
+ {
+ _memoryCache.Remove(key);
+ }
+ else
+ {
+ HttpRuntime.Cache.Remove(key);
+ }
+
_keyTracker.Remove(key);
}
@@ -88,14 +130,21 @@ namespace Umbraco.Core.Persistence.Caching
var keys = new string[_keyTracker.Count];
_keyTracker.CopyTo(keys, 0);
var keysToRemove = new List();
- foreach (var key in keys.Where(x => x.StartsWith(string.Format("{0}-", type.Name))))
+ foreach (var key in keys.Where(x => x.StartsWith(string.Format("{0}{1}-", CacheItemPrefix, type.Name))))
{
_keyTracker.Remove(key);
keysToRemove.Add(key);
}
foreach (var key in keysToRemove)
{
- _memoryCache.Remove(key);
+ if (HttpContext.Current == null)
+ {
+ _memoryCache.Remove(key);
+ }
+ else
+ {
+ HttpRuntime.Cache.Remove(key);
+ }
}
}
}
@@ -105,19 +154,40 @@ namespace Umbraco.Core.Persistence.Caching
using (new WriteLock(ClearLock))
{
_keyTracker.Clear();
- _memoryCache.DisposeIfDisposable();
- _memoryCache = new MemoryCache("in-memory");
+
+ if (HttpContext.Current == null)
+ {
+ _memoryCache.DisposeIfDisposable();
+ _memoryCache = new MemoryCache("in-memory");
+ }
+ else
+ {
+ foreach (DictionaryEntry c in HttpRuntime.Cache)
+ {
+ if (c.Key is string && ((string)c.Key).InvariantStartsWith(CacheItemPrefix))
+ {
+ if (HttpRuntime.Cache[(string)c.Key] == null) return;
+ HttpRuntime.Cache.Remove((string)c.Key);
+ }
+ }
+ }
}
}
+ ///
+ /// 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.
+ ///
+ private const string CacheItemPrefix = "umbrtmche_";
+
private string GetCompositeId(Type type, Guid id)
{
- return string.Format("{0}-{1}", type.Name, id.ToString());
+ return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToString());
}
private string GetCompositeId(Type type, int id)
{
- return string.Format("{0}-{1}", type.Name, id.ToGuid());
+ return string.Format("{0}{1}-{2}", CacheItemPrefix, type.Name, id.ToGuid());
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs
index 73c7b290f9..d8d4b1c7e2 100644
--- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs
+++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs
@@ -118,7 +118,7 @@ namespace Umbraco.Core.Persistence
public virtual ITemplateRepository CreateTemplateRepository(IDatabaseUnitOfWork uow)
{
- return new TemplateRepository(uow, NullCacheProvider.Current);
+ return new TemplateRepository(uow, RuntimeCacheProvider.Current);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index b3f8c9dec3..ed47bc76d6 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -1122,6 +1122,9 @@ namespace Umbraco.Core.Services
Audit.Add(AuditTypes.Copy, "Copy Content performed by user", content.WriterId, content.Id);
+
+ //TODO: Don't think we need this here because cache should be cleared by the event listeners
+ // and the correct ICacheRefreshers!?
RuntimeCacheProvider.Current.Clear();
return copy;
diff --git a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs b/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs
index 0aec83104a..42026a7523 100644
--- a/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/TemplateCacheRefresher.cs
@@ -1,6 +1,8 @@
using System;
using Umbraco.Core;
using Umbraco.Core.Cache;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.Caching;
using umbraco;
using umbraco.interfaces;
@@ -55,6 +57,10 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.ClearCacheItem(
string.Format("{0}{1}", CacheKeys.TemplateBusinessLogicCacheKey, id));
+
+ //need to clear the runtime cache for template instances
+ //NOTE: This is temp until we implement the correct ApplicationCache and then we can remove the RuntimeCache, etc...
+ RuntimeCacheProvider.Current.Clear(typeof(ITemplate));
}
}