2019-01-17 11:01:23 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
2021-02-18 11:06:02 +01:00
|
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
|
using Umbraco.Cms.Core.Models.Entities;
|
2021-08-11 19:11:35 +02:00
|
|
|
|
using Umbraco.Extensions;
|
2019-01-17 11:01:23 +01:00
|
|
|
|
|
2021-02-18 11:06:02 +01:00
|
|
|
|
namespace Umbraco.Cms.Core.Cache
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
/// <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>
|
2021-08-11 19:11:35 +02:00
|
|
|
|
public class DeepCloneAppCache : IAppPolicyCache, IDisposable
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
2021-04-20 16:41:11 +10:00
|
|
|
|
private bool _disposedValue;
|
|
|
|
|
|
|
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-11-07 19:39:20 +11:00
|
|
|
|
private IAppPolicyCache InnerCache { get; }
|
2019-01-17 11:01:23 +01:00
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2021-12-16 13:44:20 +01:00
|
|
|
|
public object? Get(string key)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
var item = InnerCache.Get(key);
|
|
|
|
|
|
return CheckCloneableAndTracksChanges(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2021-12-16 13:44:20 +01:00
|
|
|
|
public object? Get(string key, Func<object?> factory)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
var cached = InnerCache.Get(key, () =>
|
|
|
|
|
|
{
|
2019-11-07 19:16:45 +11:00
|
|
|
|
var result = SafeLazy.GetSafeLazy(factory);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
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 />
|
2021-12-16 13:44:20 +01:00
|
|
|
|
public IEnumerable<object?> SearchByKey(string keyStartsWith)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
return InnerCache.SearchByKey(keyStartsWith)
|
|
|
|
|
|
.Select(CheckCloneableAndTracksChanges);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2021-12-16 13:44:20 +01:00
|
|
|
|
public IEnumerable<object?> SearchByRegex(string regex)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
return InnerCache.SearchByRegex(regex)
|
|
|
|
|
|
.Select(CheckCloneableAndTracksChanges);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2021-12-16 13:44:20 +01: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, () =>
|
|
|
|
|
|
{
|
2019-11-07 19:16:45 +11:00
|
|
|
|
var result = SafeLazy.GetSafeLazy(factory);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
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
|
2020-03-19 08:53:18 +01:00
|
|
|
|
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
|
|
|
|
|
|
// 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 />
|
2021-12-16 13:44:20 +01: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, () =>
|
|
|
|
|
|
{
|
2019-11-07 19:16:45 +11:00
|
|
|
|
var result = SafeLazy.GetSafeLazy(factory);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
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 />
|
2020-03-19 08:53:18 +01:00
|
|
|
|
public void ClearOfType(Type type)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
2020-03-19 08:53:18 +01:00
|
|
|
|
InnerCache.ClearOfType(type);
|
2019-01-17 11:01:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-16 13:44:20 +01:00
|
|
|
|
private static object? CheckCloneableAndTracksChanges(object? input)
|
2019-01-17 11:01:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (input is IDeepCloneable cloneable)
|
|
|
|
|
|
{
|
|
|
|
|
|
input = cloneable.DeepClone();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reset dirty initial properties
|
|
|
|
|
|
if (input is IRememberBeingDirty tracksChanges)
|
|
|
|
|
|
{
|
|
|
|
|
|
tracksChanges.ResetDirtyProperties(false);
|
|
|
|
|
|
input = tracksChanges;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return input;
|
|
|
|
|
|
}
|
2021-04-20 16:41:11 +10:00
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_disposedValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (disposing)
|
|
|
|
|
|
{
|
|
|
|
|
|
InnerCache.DisposeIfDisposable();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_disposedValue = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
|
|
|
|
Dispose(disposing: true);
|
|
|
|
|
|
}
|
2019-01-17 11:01:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|