2013-08-12 15:06:12 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Web;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Cache
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A cache provider that caches items in the HttpContext.Items
|
|
|
|
|
|
/// </summary>
|
2013-12-16 16:21:38 +11:00
|
|
|
|
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
2013-08-12 15:06:12 +02:00
|
|
|
|
{
|
2014-06-28 12:09:54 +02:00
|
|
|
|
// context provider
|
|
|
|
|
|
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
|
|
|
|
|
// that is initialized with a method that returns the "current" context.
|
|
|
|
|
|
// NOTE
|
|
|
|
|
|
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
|
|
|
|
|
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
|
|
|
|
|
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
|
|
|
|
|
//private readonly Func<HttpContextBase> _context;
|
2013-08-12 15:06:12 +02:00
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
// NOTE
|
|
|
|
|
|
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
|
|
|
|
|
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
|
|
|
|
|
// _context1 is null?
|
|
|
|
|
|
//private readonly HttpContextBase _context1;
|
|
|
|
|
|
//private readonly Func<HttpContext> _context2;
|
|
|
|
|
|
private readonly HttpContextBase _context;
|
|
|
|
|
|
|
|
|
|
|
|
private IDictionary ContextItems
|
2013-08-12 15:06:12 +02:00
|
|
|
|
{
|
2014-06-28 12:09:54 +02:00
|
|
|
|
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
|
|
|
|
|
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
2013-08-12 15:06:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
// for unit tests
|
|
|
|
|
|
public HttpRequestCacheProvider(HttpContextBase context)
|
2013-08-12 15:06:12 +02:00
|
|
|
|
{
|
|
|
|
|
|
_context = context;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
// main constructor
|
|
|
|
|
|
// will use HttpContext.Current
|
|
|
|
|
|
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
|
|
|
|
|
{
|
|
|
|
|
|
//_context2 = context;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-27 12:16:41 +02:00
|
|
|
|
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
2013-08-12 15:06:12 +02:00
|
|
|
|
{
|
2014-05-27 12:16:41 +02:00
|
|
|
|
const string prefix = CacheItemPrefix + "-";
|
2014-06-28 12:09:54 +02:00
|
|
|
|
return ContextItems.Cast<DictionaryEntry>()
|
2014-05-27 12:16:41 +02:00
|
|
|
|
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void RemoveEntry(string key)
|
|
|
|
|
|
{
|
2014-06-28 12:09:54 +02:00
|
|
|
|
ContextItems.Remove(key);
|
2014-05-27 12:16:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override object GetEntry(string key)
|
|
|
|
|
|
{
|
2014-06-28 12:09:54 +02:00
|
|
|
|
return ContextItems[key];
|
2013-08-12 15:06:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
#region Lock
|
|
|
|
|
|
|
|
|
|
|
|
protected override IDisposable ReadLock
|
|
|
|
|
|
{
|
|
|
|
|
|
// there's no difference between ReadLock and WriteLock here
|
|
|
|
|
|
get { return WriteLock; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override IDisposable WriteLock
|
|
|
|
|
|
{
|
|
|
|
|
|
// NOTE
|
|
|
|
|
|
// could think about just overriding base.Locker to return a different
|
|
|
|
|
|
// object but then we'd create a ReaderWriterLockSlim per request,
|
|
|
|
|
|
// which is less efficient than just using a basic monitor lock.
|
|
|
|
|
|
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2014-06-28 18:32:56 +02:00
|
|
|
|
return new MonitorLock(ContextItems.SyncRoot);
|
2014-06-28 12:09:54 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2014-05-27 12:16:41 +02:00
|
|
|
|
#region Get
|
|
|
|
|
|
|
2013-08-12 15:06:12 +02:00
|
|
|
|
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
|
|
|
|
|
{
|
2014-05-27 12:16:41 +02:00
|
|
|
|
cacheKey = GetCacheKey(cacheKey);
|
|
|
|
|
|
|
|
|
|
|
|
Lazy<object> result;
|
|
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
using (WriteLock)
|
2014-05-27 12:16:41 +02:00
|
|
|
|
{
|
2014-06-28 12:09:54 +02:00
|
|
|
|
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
2014-05-27 12:16:41 +02:00
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
|
|
|
|
|
// do nothing here - means that if creation throws, a race condition could cause
|
|
|
|
|
|
// more than one thread to reach the return statement below and throw - accepted.
|
2015-07-08 18:14:31 +02:00
|
|
|
|
|
2014-06-28 12:09:54 +02:00
|
|
|
|
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
|
|
|
|
|
{
|
2015-05-19 09:16:05 +02:00
|
|
|
|
result = GetSafeLazy(getCacheItem);
|
2014-06-28 12:09:54 +02:00
|
|
|
|
ContextItems[cacheKey] = result;
|
2014-05-27 12:16:41 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-05-19 09:16:05 +02:00
|
|
|
|
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
|
|
|
|
|
|
// exceptions (but try again and again) and silently eat them - however at
|
|
|
|
|
|
// some point we have to report them - so need to re-throw here
|
|
|
|
|
|
|
|
|
|
|
|
// this does not throw anymore
|
|
|
|
|
|
//return result.Value;
|
|
|
|
|
|
|
|
|
|
|
|
var value = result.Value; // will not throw (safe lazy)
|
|
|
|
|
|
var eh = value as ExceptionHolder;
|
|
|
|
|
|
if (eh != null) throw eh.Exception; // throw once!
|
|
|
|
|
|
return value;
|
2013-08-12 15:06:12 +02:00
|
|
|
|
}
|
2015-07-08 18:14:31 +02:00
|
|
|
|
|
2014-05-27 12:16:41 +02:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Insert
|
|
|
|
|
|
#endregion
|
2013-08-12 15:06:12 +02:00
|
|
|
|
|
|
|
|
|
|
}
|
2013-08-08 19:46:58 +10:00
|
|
|
|
}
|