From ace5c54da217fdcf5da463dfa5eda174f0874e6b Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 10 Nov 2023 11:19:57 +0100 Subject: [PATCH] Remove unused/untested dependent files support from IAppPolicyCache --- src/Umbraco.Core/Cache/AppCacheExtensions.cs | 10 +- src/Umbraco.Core/Cache/DeepCloneAppCache.cs | 10 +- src/Umbraco.Core/Cache/IAppPolicyCache.cs | 8 +- src/Umbraco.Core/Cache/NoAppCache.cs | 4 +- src/Umbraco.Core/Cache/ObjectCacheAppCache.cs | 307 +++++++----------- src/Umbraco.Web.UI.New.Client | 2 +- .../Cache/DefaultCachePolicyTests.cs | 6 +- .../Cache/FullDataSetCachePolicyTests.cs | 10 +- .../Cache/SingleItemsOnlyCachePolicyTests.cs | 6 +- 9 files changed, 135 insertions(+), 228 deletions(-) diff --git a/src/Umbraco.Core/Cache/AppCacheExtensions.cs b/src/Umbraco.Core/Cache/AppCacheExtensions.cs index d70e293d4a..aa1c7e3333 100644 --- a/src/Umbraco.Core/Cache/AppCacheExtensions.cs +++ b/src/Umbraco.Core/Cache/AppCacheExtensions.cs @@ -12,10 +12,9 @@ public static class AppCacheExtensions string cacheKey, Func getCacheItem, TimeSpan? timeout, - bool isSliding = false, - string[]? dependentFiles = null) + bool isSliding = false) { - var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding, dependentFiles); + var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding); return result == null ? default : result.TryConvertTo().Result; } @@ -24,9 +23,8 @@ public static class AppCacheExtensions string cacheKey, Func getCacheItem, TimeSpan? timeout = null, - bool isSliding = false, - string[]? dependentFiles = null) => - provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding, dependentFiles); + bool isSliding = false) => + provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding); public static IEnumerable GetCacheItemsByKeySearch(this IAppCache provider, string keyStartsWith) { diff --git a/src/Umbraco.Core/Cache/DeepCloneAppCache.cs b/src/Umbraco.Core/Cache/DeepCloneAppCache.cs index da86be4b70..acbc5b37a7 100644 --- a/src/Umbraco.Core/Cache/DeepCloneAppCache.cs +++ b/src/Umbraco.Core/Cache/DeepCloneAppCache.cs @@ -65,7 +65,7 @@ public class DeepCloneAppCache : IAppPolicyCache, IDisposable .Select(CheckCloneableAndTracksChanges); /// - public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[]? dependentFiles = null) + public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false) { var cached = InnerCache.Get( key, @@ -81,15 +81,14 @@ public class DeepCloneAppCache : IAppPolicyCache, IDisposable // clone / reset to go into the cache }, timeout, - isSliding, - dependentFiles); + isSliding); // clone / reset to go into the cache return CheckCloneableAndTracksChanges(cached); } /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[]? dependentFiles = null) => + public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false) => InnerCache.Insert( key, () => @@ -102,8 +101,7 @@ public class DeepCloneAppCache : IAppPolicyCache, IDisposable return value == null ? null : CheckCloneableAndTracksChanges(value); }, timeout, - isSliding, - dependentFiles); + isSliding); /// public void Clear() => InnerCache.Clear(); diff --git a/src/Umbraco.Core/Cache/IAppPolicyCache.cs b/src/Umbraco.Core/Cache/IAppPolicyCache.cs index 1d0044c057..afef4a11fc 100644 --- a/src/Umbraco.Core/Cache/IAppPolicyCache.cs +++ b/src/Umbraco.Core/Cache/IAppPolicyCache.cs @@ -16,14 +16,12 @@ public interface IAppPolicyCache : IAppCache /// A factory function that can create the item. /// An optional cache timeout. /// An optional value indicating whether the cache timeout is sliding (default is false). - /// Files the cache entry depends on. /// The item. object? Get( string key, Func factory, TimeSpan? timeout, - bool isSliding = false, - string[]? dependentFiles = null); + bool isSliding = false); /// /// Inserts an item. @@ -32,11 +30,9 @@ public interface IAppPolicyCache : IAppCache /// A factory function that can create the item. /// An optional cache timeout. /// An optional value indicating whether the cache timeout is sliding (default is false). - /// Files the cache entry depends on. void Insert( string key, Func factory, TimeSpan? timeout = null, - bool isSliding = false, - string[]? dependentFiles = null); + bool isSliding = false); } diff --git a/src/Umbraco.Core/Cache/NoAppCache.cs b/src/Umbraco.Core/Cache/NoAppCache.cs index 70edbcf61d..f140680b8c 100644 --- a/src/Umbraco.Core/Cache/NoAppCache.cs +++ b/src/Umbraco.Core/Cache/NoAppCache.cs @@ -32,10 +32,10 @@ public class NoAppCache : IAppPolicyCache, IRequestCache public IEnumerable SearchByRegex(string regex) => Enumerable.Empty(); /// - public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[]? dependentFiles = null) => factory(); + public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false) => factory(); /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[]? dependentFiles = null) + public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false) { } diff --git a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs index dcd83ece94..5907e1e13b 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs @@ -1,31 +1,48 @@ -using System.Runtime.Caching; using System.Text.RegularExpressions; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Cache; /// -/// Implements on top of a . +/// Implements on top of a . /// public class ObjectCacheAppCache : IAppPolicyCache, IDisposable { + private readonly IOptions _options; + private readonly ISet _keys = new HashSet(); private readonly ReaderWriterLockSlim _locker = new(LockRecursionPolicy.SupportsRecursion); private bool _disposedValue; /// - /// Initializes a new instance of the . + /// Gets the internal memory cache, for tests only! /// - public ObjectCacheAppCache() => - - // the MemoryCache is created with name "in-memory". That name is - // used to retrieve configuration options. It does not identify the memory cache, i.e. - // each instance of this class has its own, independent, memory cache. - MemoryCache = new MemoryCache("in-memory"); + /// + /// The memory cache. + /// + internal MemoryCache MemoryCache { get; private set; } /// - /// Gets the internal memory cache, for tests only! + /// Initializes a new instance of the . /// - public ObjectCache MemoryCache { get; private set; } + public ObjectCacheAppCache() + : this(Options.Create(new MemoryCacheOptions()), NullLoggerFactory.Instance) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The options. + /// The logger factory. + public ObjectCacheAppCache(IOptions options, ILoggerFactory loggerFactory) + { + _options = options; + + MemoryCache = new MemoryCache(_options, loggerFactory); + } /// public object? Get(string key) @@ -34,6 +51,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable try { _locker.EnterReadLock(); + result = MemoryCache.Get(key) as Lazy; // null if key not found } finally @@ -51,14 +69,21 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable public object? Get(string key, Func factory) => Get(key, factory, null); /// - public IEnumerable SearchByKey(string keyStartsWith) + public IEnumerable SearchByKey(string keyStartsWith) => SearchByPredicate(key => key.InvariantStartsWith(keyStartsWith)); + + /// + public IEnumerable SearchByRegex(string regex) => SearchByPredicate(new Regex(regex, RegexOptions.Compiled).IsMatch); + + private IEnumerable SearchByPredicate(Func predicate) { - KeyValuePair[] entries; + object[] entries; try { _locker.EnterReadLock(); - entries = MemoryCache - .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) + + entries = _keys.Where(predicate) + .Select(key => MemoryCache.Get(key)) + .WhereNotNull() .ToArray(); // evaluate while locked } finally @@ -70,40 +95,13 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } return entries - .Select(x => SafeLazy.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null - .Where(x => x != null) // backward compat, don't store null values in the cache - .ToList()!; + .Select(x => SafeLazy.GetSafeLazyValue((Lazy)x)) // return exceptions as null + .WhereNotNull() // backward compat, don't store null values in the cache + .ToList(); } /// - public IEnumerable SearchByRegex(string regex) - { - var compiled = new Regex(regex, RegexOptions.Compiled); - - KeyValuePair[] entries; - try - { - _locker.EnterReadLock(); - entries = MemoryCache - .Where(x => compiled.IsMatch(x.Key)) - .ToArray(); // evaluate while locked - } - finally - { - if (_locker.IsReadLockHeld) - { - _locker.ExitReadLock(); - } - } - - return entries - .Select(x => SafeLazy.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null - .Where(x => x != null) // backward compat, don't store null values in the cache - .ToList()!; - } - - /// - public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false, string[]? dependentFiles = null) + public object? Get(string key, Func factory, TimeSpan? timeout, bool isSliding = false) { // see notes in HttpRuntimeAppCache Lazy? result; @@ -118,14 +116,15 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) { result = SafeLazy.GetSafeLazy(factory); - CacheItemPolicy policy = GetPolicy(timeout, isSliding, dependentFiles); + MemoryCacheEntryOptions options = GetOptions(timeout, isSliding); try { _locker.EnterWriteLock(); // NOTE: This does an add or update - MemoryCache.Set(key, result, policy); + MemoryCache.Set(key, result, options); + _keys.Add(key); } finally { @@ -155,7 +154,7 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } /// - public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false, string[]? dependentFiles = null) + public void Insert(string key, Func factory, TimeSpan? timeout = null, bool isSliding = false) { // NOTE - here also we must insert a Lazy but we can evaluate it right now // and make sure we don't store a null value. @@ -166,10 +165,11 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable return; // do not store null values (backward compat) } - CacheItemPolicy policy = GetPolicy(timeout, isSliding, dependentFiles); + MemoryCacheEntryOptions options = GetOptions(timeout, isSliding); // NOTE: This does an add or update - MemoryCache.Set(key, result, policy); + MemoryCache.Set(key, result, options); + _keys.Add(key); } /// @@ -178,8 +178,10 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable try { _locker.EnterWriteLock(); - MemoryCache.DisposeIfDisposable(); - MemoryCache = new MemoryCache("in-memory"); + + MemoryCache.Dispose(); + MemoryCache = new MemoryCache(_options); + _keys.Clear(); } finally { @@ -196,12 +198,9 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable try { _locker.EnterWriteLock(); - if (MemoryCache[key] == null) - { - return; - } MemoryCache.Remove(key); + _keys.Remove(key); } finally { @@ -221,159 +220,75 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } var isInterface = type.IsInterface; - try - { - _locker.EnterWriteLock(); - // ToArray required to remove - foreach (var key in MemoryCache - .Where(x => - { - // x.Value is Lazy and not null, its value may be null - // remove null values as well, does not hurt - // get non-created as NonCreatedValue & exceptions as null - var value = SafeLazy.GetSafeLazyValue((Lazy)x.Value, true); - - // if T is an interface remove anything that implements that interface - // otherwise remove exact types (not inherited types) - return value == null || - (isInterface ? type.IsInstanceOfType(value) : value.GetType() == type); - }) - .Select(x => x.Key) - .ToArray()) - { - MemoryCache.Remove(key); - } - } - finally + ClearByPredicate(key => { - if (_locker.IsWriteLockHeld) + var entry = MemoryCache.Get(key); + if (entry is null) { - _locker.ExitWriteLock(); + return false; } - } + + // x.Value is Lazy and not null, its value may be null + // remove null values as well, does not hurt + // get non-created as NonCreatedValue & exceptions as null + var value = SafeLazy.GetSafeLazyValue((Lazy)entry, true); + + // if T is an interface remove anything that implements that interface + // otherwise remove exact types (not inherited types) + return value == null || (isInterface ? type.IsInstanceOfType(value) : value.GetType() == type); + }); } /// - public virtual void ClearOfType() - { - try - { - _locker.EnterWriteLock(); - Type typeOfT = typeof(T); - var isInterface = typeOfT.IsInterface; - - // ToArray required to remove - foreach (var key in MemoryCache - .Where(x => - { - // x.Value is Lazy and not null, its value may be null - // remove null values as well, does not hurt - // get non-created as NonCreatedValue & exceptions as null - var value = SafeLazy.GetSafeLazyValue((Lazy)x.Value, true); - - // if T is an interface remove anything that implements that interface - // otherwise remove exact types (not inherited types) - return value == null || (isInterface ? value is T : value.GetType() == typeOfT); - }) - .Select(x => x.Key) - .ToArray()) - { - MemoryCache.Remove(key); - } - } - finally - { - if (_locker.IsWriteLockHeld) - { - _locker.ExitWriteLock(); - } - } - } + public virtual void ClearOfType() => ClearOfType(typeof(T)); /// public virtual void ClearOfType(Func predicate) { - try - { - _locker.EnterWriteLock(); - Type typeOfT = typeof(T); - var isInterface = typeOfT.IsInterface; + Type type = typeof(T); + var isInterface = type.IsInterface; - // ToArray required to remove - foreach (var key in MemoryCache - .Where(x => - { - // x.Value is Lazy and not null, its value may be null - // remove null values as well, does not hurt - // get non-created as NonCreatedValue & exceptions as null - var value = SafeLazy.GetSafeLazyValue((Lazy)x.Value, true); - if (value == null) - { - return true; - } - - // if T is an interface remove anything that implements that interface - // otherwise remove exact types (not inherited types) - return (isInterface ? value is T : value.GetType() == typeOfT) - && predicate(x.Key, (T)value); - }) - .Select(x => x.Key) - .ToArray()) - { - MemoryCache.Remove(key); - } - } - finally + ClearByPredicate(key => { - if (_locker.IsWriteLockHeld) + var entry = MemoryCache.Get(key); + if (entry is null) { - _locker.ExitWriteLock(); + return false; } - } + + // x.Value is Lazy and not null, its value may be null + // remove null values as well, does not hurt + // get non-created as NonCreatedValue & exceptions as null + var value = SafeLazy.GetSafeLazyValue((Lazy)entry, true); + if (value == null) + { + return true; + } + + // if T is an interface remove anything that implements that interface + // otherwise remove exact types (not inherited types) + return (isInterface ? value is T : value.GetType() == type) && predicate(key, (T)value); + }); } /// - public virtual void ClearByKey(string keyStartsWith) + public virtual void ClearByKey(string keyStartsWith) => ClearByPredicate(x => x.InvariantStartsWith(keyStartsWith)); + + /// + public virtual void ClearByRegex(string regex) => ClearByPredicate(new Regex(regex, RegexOptions.Compiled).IsMatch); + + private void ClearByPredicate(Func predicate) { try { _locker.EnterWriteLock(); // ToArray required to remove - foreach (var key in MemoryCache - .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) - .Select(x => x.Key) - .ToArray()) - { - MemoryCache.Remove(key); - } - } - finally - { - if (_locker.IsWriteLockHeld) - { - _locker.ExitWriteLock(); - } - } - } - - /// - public virtual void ClearByRegex(string regex) - { - var compiled = new Regex(regex, RegexOptions.Compiled); - - try - { - _locker.EnterWriteLock(); - - // ToArray required to remove - foreach (var key in MemoryCache - .Where(x => compiled.IsMatch(x.Key)) - .Select(x => x.Key) - .ToArray()) + foreach (var key in _keys.Where(predicate).ToArray()) { MemoryCache.Remove(key); + _keys.Remove(key); } } finally @@ -386,7 +301,6 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable } public void Dispose() => - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(true); @@ -397,27 +311,28 @@ public class ObjectCacheAppCache : IAppPolicyCache, IDisposable if (disposing) { _locker.Dispose(); + MemoryCache.Dispose(); } _disposedValue = true; } } - private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, string[]? dependentFiles = null) + private MemoryCacheEntryOptions GetOptions(TimeSpan? timeout, bool isSliding) { - DateTimeOffset absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : - timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value); - TimeSpan sliding = isSliding == false - ? ObjectCache.NoSlidingExpiration - : timeout ?? ObjectCache.NoSlidingExpiration; + var options = new MemoryCacheEntryOptions(); - var policy = new CacheItemPolicy { AbsoluteExpiration = absolute, SlidingExpiration = sliding }; - - if (dependentFiles != null && dependentFiles.Any()) + // Configure time based expiration + if (isSliding) { - policy.ChangeMonitors.Add(new HostFileChangeMonitor(dependentFiles.ToList())); + options.SlidingExpiration = timeout; + } + else + { + options.AbsoluteExpirationRelativeToNow = timeout; } - return policy; + // Ensure key is removed from set when evicted from cache + return options.RegisterPostEvictionCallback((key, _, _, _) => _keys.Remove((string)key)); } } diff --git a/src/Umbraco.Web.UI.New.Client b/src/Umbraco.Web.UI.New.Client index 5798c3081e..d48a222423 160000 --- a/src/Umbraco.Web.UI.New.Client +++ b/src/Umbraco.Web.UI.New.Client @@ -1 +1 @@ -Subproject commit 5798c3081e1e787291263b1404b251d40508ae51 +Subproject commit d48a2224236947619864cb043bb4fbb468192a9e diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DefaultCachePolicyTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DefaultCachePolicyTests.cs index 1a296a95cb..fc156e431d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DefaultCachePolicyTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DefaultCachePolicyTests.cs @@ -32,7 +32,7 @@ public class DefaultCachePolicyTests { var isCached = false; var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) .Callback(() => isCached = true); var defaultPolicy = @@ -60,8 +60,8 @@ public class DefaultCachePolicyTests { var cached = new List(); var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string cacheKey, Func o, TimeSpan? t, bool b, string[] s) => cached.Add(cacheKey)); + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((string cacheKey, Func o, TimeSpan? t, bool b) => cached.Add(cacheKey)); cache.Setup(x => x.SearchByKey(It.IsAny())).Returns(new AuditItem[] { }); var defaultPolicy = diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/FullDataSetCachePolicyTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/FullDataSetCachePolicyTests.cs index 73cd6eccd7..6547be90df 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/FullDataSetCachePolicyTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/FullDataSetCachePolicyTests.cs @@ -41,7 +41,7 @@ public class FullDataSetCachePolicyTests var isCached = false; var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) .Callback(() => isCached = true); var policy = @@ -79,8 +79,8 @@ public class FullDataSetCachePolicyTests IList list = null; var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string cacheKey, Func o, TimeSpan? t, bool b, string[] s) => + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((string cacheKey, Func o, TimeSpan? t, bool b) => { cached.Add(cacheKey); @@ -121,8 +121,8 @@ public class FullDataSetCachePolicyTests IList list = null; var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string cacheKey, Func o, TimeSpan? t, bool b, string[] s) => + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((string cacheKey, Func o, TimeSpan? t, bool b) => { cached.Add(cacheKey); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/SingleItemsOnlyCachePolicyTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/SingleItemsOnlyCachePolicyTests.cs index ef2ce5b72c..67c86a3af8 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/SingleItemsOnlyCachePolicyTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/SingleItemsOnlyCachePolicyTests.cs @@ -32,8 +32,8 @@ public class SingleItemsOnlyCachePolicyTests { var cached = new List(); var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((string cacheKey, Func o, TimeSpan? t, bool b, string[] s) => cached.Add(cacheKey)); + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((string cacheKey, Func o, TimeSpan? t, bool b) => cached.Add(cacheKey)); cache.Setup(x => x.SearchByKey(It.IsAny())).Returns(new AuditItem[] { }); var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy(cache.Object, DefaultAccessor, new RepositoryCachePolicyOptions()); @@ -54,7 +54,7 @@ public class SingleItemsOnlyCachePolicyTests { var isCached = false; var cache = new Mock(); - cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + cache.Setup(x => x.Insert(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny())) .Callback(() => isCached = true); var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy(cache.Object, DefaultAccessor, new RepositoryCachePolicyOptions());