2019-01-17 11:01:23 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using Umbraco.Core.Models;
|
|
|
|
|
|
using Umbraco.Core.Models.Entities;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Cache
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2019-01-18 07:56:38 +01:00
|
|
|
|
/// Implements <see cref="IAppPolicyCache"/> by wrapping an inner other <see cref="IAppPolicyCache"/>
|
2019-01-17 11:01:23 +01:00
|
|
|
|
/// instance, and ensuring that all inserts and returns are deep cloned copies of the cache item,
|
|
|
|
|
|
/// when the item is deep-cloneable.
|
|
|
|
|
|
/// </summary>
|
2019-01-18 07:56:38 +01:00
|
|
|
|
internal class DeepCloneAppCache : IAppPolicyCache
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initializes a new instance of the <see cref="DeepCloneAppCache"/> class.
|
|
|
|
|
|
/// </summary>
|
2019-01-18 07:56:38 +01:00
|
|
|
|
public DeepCloneAppCache(IAppPolicyCache innerCache)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
var type = typeof (DeepCloneAppCache);
|
|
|
|
|
|
|
|
|
|
|
|
if (innerCache.GetType() == type)
|
|
|
|
|
|
throw new InvalidOperationException($"A {type} cannot wrap another instance of itself.");
|
|
|
|
|
|
|
|
|
|
|
|
InnerCache = innerCache;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets the inner cache.
|
|
|
|
|
|
/// </summary>
|
2019-01-18 07:56:38 +01:00
|
|
|
|
public IAppPolicyCache InnerCache { get; }
|
2019-01-17 11:01:23 +01:00
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public object Get(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
var item = InnerCache.Get(key);
|
|
|
|
|
|
return CheckCloneableAndTracksChanges(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public object Get(string key, Func<object> factory)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cached = InnerCache.Get(key, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
|
|
|
|
|
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
|
|
|
|
|
// do not store null values (backward compat), clone / reset to go into the cache
|
|
|
|
|
|
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
|
|
|
|
|
});
|
|
|
|
|
|
return CheckCloneableAndTracksChanges(cached);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
|
|
|
|
|
{
|
|
|
|
|
|
return InnerCache.SearchByKey(keyStartsWith)
|
|
|
|
|
|
.Select(CheckCloneableAndTracksChanges);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public IEnumerable<object> SearchByRegex(string regex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return InnerCache.SearchByRegex(regex)
|
|
|
|
|
|
.Select(CheckCloneableAndTracksChanges);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2019-11-07 18:34:05 +11:00
|
|
|
|
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, string[] dependentFiles = null)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
var cached = InnerCache.Get(key, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
|
|
|
|
|
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
|
|
|
|
|
// do not store null values (backward compat), clone / reset to go into the cache
|
|
|
|
|
|
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
|
|
|
|
|
|
|
|
|
|
|
// clone / reset to go into the cache
|
2019-11-07 18:34:05 +11:00
|
|
|
|
}, timeout, isSliding, dependentFiles);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
|
|
|
|
|
|
// clone / reset to go into the cache
|
|
|
|
|
|
return CheckCloneableAndTracksChanges(cached);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2019-11-07 18:34:05 +11:00
|
|
|
|
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, string[] dependentFiles = null)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
InnerCache.Insert(key, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
|
|
|
|
|
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
|
|
|
|
|
// do not store null values (backward compat), clone / reset to go into the cache
|
|
|
|
|
|
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
2019-11-07 18:34:05 +11:00
|
|
|
|
}, timeout, isSliding, dependentFiles);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void Clear()
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void Clear(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.Clear(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ClearOfType(string typeName)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.ClearOfType(typeName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ClearOfType<T>()
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.ClearOfType<T>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ClearOfType<T>(Func<string, T, bool> predicate)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.ClearOfType<T>(predicate);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ClearByKey(string keyStartsWith)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.ClearByKey(keyStartsWith);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ClearByRegex(string regex)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.ClearByRegex(regex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static object CheckCloneableAndTracksChanges(object input)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (input is IDeepCloneable cloneable)
|
|
|
|
|
|
{
|
|
|
|
|
|
input = cloneable.DeepClone();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reset dirty initial properties
|
|
|
|
|
|
if (input is IRememberBeingDirty tracksChanges)
|
|
|
|
|
|
{
|
|
|
|
|
|
tracksChanges.ResetDirtyProperties(false);
|
|
|
|
|
|
input = tracksChanges;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return input;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|