diff --git a/LazyCache.AspNetCore/LazyCacheServiceCollectionExtensions.cs b/LazyCache.AspNetCore/LazyCacheServiceCollectionExtensions.cs
index 4eb7182..d623212 100644
--- a/LazyCache.AspNetCore/LazyCacheServiceCollectionExtensions.cs
+++ b/LazyCache.AspNetCore/LazyCacheServiceCollectionExtensions.cs
@@ -7,9 +7,21 @@
// ReSharper disable once CheckNamespace - MS guidelines say put DI registration in this NS
namespace Microsoft.Extensions.DependencyInjection
{
- // See https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheServiceCollectionExtensions.cs
+ // See https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheServiceCollectionExtensions.cs
+ ///
+ /// Set of extensions for registering LazyCache dependencies with instance.
+ ///
public static class LazyCacheServiceCollectionExtensions
{
+ ///
+ /// Register a non distributed in memory implementation of .
+ ///
+ ///
+ /// For implementation details see
+ ///
+ /// Instance of .
+ /// Modified instance of .
+ ///
public static IServiceCollection AddLazyCache(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
@@ -25,6 +37,13 @@ public static IServiceCollection AddLazyCache(this IServiceCollection services)
return services;
}
+ ///
+ /// Register a custom implementation of to the
+ ///
+ /// Instance of .
+ /// A delegate that allows users to inject their own implementation.
+ /// Modified instance of
+ /// Thrown when any of: or are null.
public static IServiceCollection AddLazyCache(this IServiceCollection services,
Func implementationFactory)
{
diff --git a/LazyCache.Ninject/LazyCacheModule.cs b/LazyCache.Ninject/LazyCacheModule.cs
index c1d7531..d2e92c5 100644
--- a/LazyCache.Ninject/LazyCacheModule.cs
+++ b/LazyCache.Ninject/LazyCacheModule.cs
@@ -6,20 +6,36 @@
namespace LazyCache
{
+ // See https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Caching.Memory/src/MemoryCacheServiceCollectionExtensions.cs
+ ///
+ /// Set of extensions for registering LazyCache dependencies with instance.
+ ///
public class LazyCacheModule : NinjectModule
{
private readonly Func implementationFactory;
+ ///
+ /// Initializes new instance of .
+ ///
+ ///
+ /// For implementation details see
+ ///
public LazyCacheModule()
{
}
+ ///
+ /// Initializes new instance of .
+ ///
+ /// A delegate that allows users to inject their own implementation.
public LazyCacheModule(Func implementationFactory)
{
this.implementationFactory = implementationFactory;
}
- // See also https://github.com/aspnet/Caching/blob/dev/src/Microsoft.Extensions.Caching.Memory/MemoryCacheServiceCollectionExtensions.cs
+ ///
+ /// Overrides and registers LazyeCache dependencies.
+ ///
public override void Load()
{
Bind>().ToConstant(Options.Create(new MemoryCacheOptions()));
diff --git a/LazyCache/CachingService.cs b/LazyCache/CachingService.cs
index df091de..0823b42 100644
--- a/LazyCache/CachingService.cs
+++ b/LazyCache/CachingService.cs
@@ -14,10 +14,18 @@ public class CachingService : IAppCache
private readonly int[] keyLocks;
+ ///
+ /// Initializes instance of .
+ ///
public CachingService() : this(DefaultCacheProvider)
{
}
+ ///
+ /// Initializes instance of .
+ ///
+ /// Lazy instance of .
+ /// Throw when is null.
public CachingService(Lazy cacheProvider)
{
this.cacheProvider = cacheProvider ?? throw new ArgumentNullException(nameof(cacheProvider));
@@ -25,20 +33,32 @@ public CachingService(Lazy cacheProvider)
keyLocks = new int[lockCount];
}
+ ///
+ /// Initializes instance of .
+ ///
+ /// Instance of .
+ /// Throw when is null.
+ public CachingService(ICacheProvider cache) : this(() => cache)
+ {
+ if (cache == null) throw new ArgumentNullException(nameof(cache));
+ }
+
+ ///
+ /// Initializes instance of .
+ ///
+ /// Delegate to be used to create and instance of .
+ /// Thrown if is null.
public CachingService(Func cacheProviderFactory)
{
if (cacheProviderFactory == null) throw new ArgumentNullException(nameof(cacheProviderFactory));
cacheProvider = new Lazy(cacheProviderFactory);
var lockCount = Math.Max(Environment.ProcessorCount * 8, 32);
keyLocks = new int[lockCount];
-
- }
-
- public CachingService(ICacheProvider cache) : this(() => cache)
- {
- if (cache == null) throw new ArgumentNullException(nameof(cache));
}
+ ///
+ /// Default cache provider instance: .
+ ///
public static Lazy DefaultCacheProvider { get; set; }
= new Lazy(() =>
new MemoryCacheProvider(
@@ -46,9 +66,6 @@ public CachingService(ICacheProvider cache) : this(() => cache)
new MemoryCacheOptions())
));
- ///
- /// Seconds to cache objects for by default
- ///
[Obsolete("DefaultCacheDuration has been replaced with DefaultCacheDurationSeconds")]
public virtual int DefaultCacheDuration
{
@@ -57,10 +74,144 @@ public virtual int DefaultCacheDuration
}
///
- /// Policy defining how long items should be cached for unless specified
+ /// Gets an instance of .
+ ///
+ ///
+ /// Default implementation .
+ ///
+ public virtual ICacheProvider CacheProvider => cacheProvider.Value;
+
+ ///
+ /// Defines the value for .
///
public virtual CacheDefaults DefaultCachePolicy { get; set; } = new CacheDefaults();
+ ///
+ /// Unwraps the lazy object and return its value.
+ ///
+ /// Item type.
+ /// An item.
+ /// Determines whether item type has changed from being .
+ /// Unwrapped item value.
+ protected virtual T GetValueFromLazy(object item, out bool valueHasChangedType)
+ {
+ valueHasChangedType = false;
+ switch (item)
+ {
+ case Lazy lazy:
+ return lazy.Value;
+ case T variable:
+ return variable;
+ case AsyncLazy asyncLazy:
+ // this is async to sync - and should not really happen as long as GetOrAddAsync is used for an async value.
+ // Only happens when you cache something async and then try and grab it again later using the non async methods.
+ return asyncLazy.Value.ConfigureAwait(false).GetAwaiter().GetResult();
+ case Task task:
+ return task.Result;
+ }
+
+ // if they have cached something else with the same key we need to tell caller to reset the cached item
+ // although this is probably not the fastest this should not get called on the main use case
+ // where you just hit the first switch case above.
+ var itemsType = item?.GetType();
+ if (itemsType != null && itemsType.IsGenericType && itemsType.GetGenericTypeDefinition() == typeof(Lazy<>))
+ {
+ valueHasChangedType = true;
+ }
+
+ return default(T);
+ }
+
+ ///
+ /// Unwraps the lazy object and return a task.
+ ///
+ /// Item type.
+ /// Lazy item or a task.
+ /// Signals if the value has changed its type.
+ /// A task
+ protected virtual Task GetValueFromAsyncLazy(object item, out bool valueHasChangedType)
+ {
+ valueHasChangedType = false;
+ switch (item)
+ {
+ case AsyncLazy asyncLazy:
+ return asyncLazy.Value;
+ case Task task:
+ return task;
+ // this is sync to async and only happens if you cache something sync and then get it later async
+ case Lazy lazy:
+ return Task.FromResult(lazy.Value);
+ case T variable:
+ return Task.FromResult(variable);
+ }
+
+ // if they have cached something else with the same key we need to tell caller to reset the cached item
+ // although this is probably not the fastest this should not get called on the main use case
+ // where you just hit the first switch case above.
+ var itemsType = item?.GetType();
+ if (itemsType != null && itemsType.IsGenericType && itemsType.GetGenericTypeDefinition() == typeof(AsyncLazy<>))
+ {
+ valueHasChangedType = true;
+ }
+
+ return Task.FromResult(default(T));
+ }
+
+ ///
+ /// Ensure that the pr object value gets unwrapped.
+ ///
+ /// Cached item type.
+ /// Collection of .
+ protected virtual void EnsureEvictionCallbackDoesNotReturnTheAsyncOrLazy(
+ IList callbackRegistrations)
+ {
+ if (callbackRegistrations != null)
+ foreach (var item in callbackRegistrations)
+ {
+ var originalCallback = item.EvictionCallback;
+ item.EvictionCallback = (key, value, reason, state) =>
+ {
+ // before the original callback we need to unwrap the Lazy that holds the cache item
+ if (value is AsyncLazy asyncCacheItem)
+ value = asyncCacheItem.IsValueCreated ? asyncCacheItem.Value : Task.FromResult(default(T));
+ else if (value is Lazy cacheItem)
+ value = cacheItem.IsValueCreated ? cacheItem.Value : default(T);
+
+ // pass the unwrapped cached value to the original callback
+ originalCallback(key, value, reason, state);
+ };
+ }
+ }
+
+ ///
+ /// Validate the key value.
+ ///
+ /// The cache key.
+ /// Thrown if key is null.
+ /// Thrown if key is empty or whitespace.
+ protected virtual void ValidateKey(string key)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+
+ if (string.IsNullOrWhiteSpace(key))
+ throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace");
+ }
+
+
+ ///
+ /// Tries to add an item to cache.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// Object that will be cached.
+ /// Instance of that can be used to configure behavior of the cached item.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
public virtual void Add(string key, T item, MemoryCacheEntryOptions policy)
{
if (item == null)
@@ -70,6 +221,14 @@ public virtual void Add(string key, T item, MemoryCacheEntryOptions policy)
CacheProvider.Set(key, item, policy);
}
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A cached object
+ /// Thrown when is null.
+ /// Thrown when is empty or whitespace.
public virtual T Get(string key)
{
ValidateKey(key);
@@ -79,6 +238,18 @@ public virtual T Get(string key)
return GetValueFromLazy(item, out _);
}
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
public virtual Task GetAsync(string key)
{
ValidateKey(key);
@@ -88,6 +259,19 @@ public virtual Task GetAsync(string key)
return GetValueFromAsyncLazy(item, out _);
}
+ ///
+ /// Tries to synchronously retrieve the object associated with the provided key if present.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// Instance that will capture the cached object value.
+ /// True if the item was found.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
public virtual bool TryGetValue(string key, out T value)
{
ValidateKey(key);
@@ -95,11 +279,44 @@ public virtual bool TryGetValue(string key, out T value)
return CacheProvider.TryGetValue(key, out value);
}
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// A cached object or null.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
public virtual T GetOrAdd(string key, Func addItemFactory)
{
return GetOrAdd(key, addItemFactory, null);
}
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// Instance of that can be used to configure behavior of the cached item.
+ /// A cached object or null.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
public virtual T GetOrAdd(string key, Func addItemFactory, MemoryCacheEntryOptions policy)
{
ValidateKey(key);
@@ -161,30 +378,42 @@ object CacheFactory(ICacheEntry entry) =>
}
}
- private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry)
- {
- if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return;
-
- var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value;
- if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration)
- entry.AbsoluteExpiration = absoluteExpiration;
- }
-
- public virtual void Remove(string key)
- {
- ValidateKey(key);
- CacheProvider.Remove(key);
- }
-
- public virtual ICacheProvider CacheProvider => cacheProvider.Value;
-
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
public virtual Task GetOrAddAsync(string key, Func> addItemFactory)
{
return GetOrAddAsync(key, addItemFactory, null);
}
- public virtual async Task GetOrAddAsync(string key, Func> addItemFactory,
- MemoryCacheEntryOptions policy)
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// Instance of that can be used to configure behavior of the cached item.
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ public virtual async Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy)
{
ValidateKey(key);
@@ -253,92 +482,29 @@ object CacheFactory(ICacheEntry entry) =>
}
}
- protected virtual T GetValueFromLazy(object item, out bool valueHasChangedType)
- {
- valueHasChangedType = false;
- switch (item)
- {
- case Lazy lazy:
- return lazy.Value;
- case T variable:
- return variable;
- case AsyncLazy asyncLazy:
- // this is async to sync - and should not really happen as long as GetOrAddAsync is used for an async
- // value. Only happens when you cache something async and then try and grab it again later using
- // the non async methods.
- return asyncLazy.Value.ConfigureAwait(false).GetAwaiter().GetResult();
- case Task task:
- return task.Result;
- }
-
- // if they have cached something else with the same key we need to tell caller to reset the cached item
- // although this is probably not the fastest this should not get called on the main use case
- // where you just hit the first switch case above.
- var itemsType = item?.GetType();
- if (itemsType != null && itemsType.IsGenericType && itemsType.GetGenericTypeDefinition() == typeof(Lazy<>))
- {
- valueHasChangedType = true;
- }
-
- return default(T);
- }
-
- protected virtual Task GetValueFromAsyncLazy(object item, out bool valueHasChangedType)
- {
- valueHasChangedType = false;
- switch (item)
- {
- case AsyncLazy asyncLazy:
- return asyncLazy.Value;
- case Task task:
- return task;
- // this is sync to async and only happens if you cache something sync and then get it later async
- case Lazy lazy:
- return Task.FromResult(lazy.Value);
- case T variable:
- return Task.FromResult(variable);
- }
-
- // if they have cached something else with the same key we need to tell caller to reset the cached item
- // although this is probably not the fastest this should not get called on the main use case
- // where you just hit the first switch case above.
- var itemsType = item?.GetType();
- if (itemsType != null && itemsType.IsGenericType && itemsType.GetGenericTypeDefinition() == typeof(AsyncLazy<>))
- {
- valueHasChangedType = true;
- }
-
- return Task.FromResult(default(T));
- }
-
- protected virtual void EnsureEvictionCallbackDoesNotReturnTheAsyncOrLazy(
- IList callbackRegistrations)
+ //
+ /// Tries to remove the object associated with the provided key.
+ ///
+ /// Cache key.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ public virtual void Remove(string key)
{
- if (callbackRegistrations != null)
- foreach (var item in callbackRegistrations)
- {
- var originalCallback = item.EvictionCallback;
- item.EvictionCallback = (key, value, reason, state) =>
- {
- // before the original callback we need to unwrap the Lazy that holds the cache item
- if (value is AsyncLazy asyncCacheItem)
- value = asyncCacheItem.IsValueCreated ? asyncCacheItem.Value : Task.FromResult(default(T));
- else if (value is Lazy cacheItem)
- value = cacheItem.IsValueCreated ? cacheItem.Value : default(T);
-
- // pass the unwrapped cached value to the original callback
- originalCallback(key, value, reason, state);
- };
- }
+ ValidateKey(key);
+ CacheProvider.Remove(key);
}
- protected virtual void ValidateKey(string key)
+ private static void SetAbsoluteExpirationFromRelative(ICacheEntry entry)
{
- if (key == null)
- throw new ArgumentNullException(nameof(key));
+ if (!entry.AbsoluteExpirationRelativeToNow.HasValue) return;
- if (string.IsNullOrWhiteSpace(key))
- throw new ArgumentOutOfRangeException(nameof(key), "Cache keys cannot be empty or whitespace");
+ var absoluteExpiration = DateTimeOffset.UtcNow + entry.AbsoluteExpirationRelativeToNow.Value;
+ if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration < entry.AbsoluteExpiration)
+ entry.AbsoluteExpiration = absoluteExpiration;
}
}
}
\ No newline at end of file
diff --git a/LazyCache/ExpirationMode.cs b/LazyCache/ExpirationMode.cs
index c85c383..7a53a66 100644
--- a/LazyCache/ExpirationMode.cs
+++ b/LazyCache/ExpirationMode.cs
@@ -1,5 +1,4 @@
using System;
-using System.Diagnostics;
namespace LazyCache
{
diff --git a/LazyCache/IAppCache.cs b/LazyCache/IAppCache.cs
index 1bf9737..1b3cecc 100644
--- a/LazyCache/IAppCache.cs
+++ b/LazyCache/IAppCache.cs
@@ -1,26 +1,160 @@
using System;
using System.Threading.Tasks;
+using LazyCache.Providers;
using Microsoft.Extensions.Caching.Memory;
namespace LazyCache
{
public interface IAppCache
{
+ ///
+ /// Gets an instance of .
+ ///
+ ///
+ /// Default implementation .
+ ///
ICacheProvider CacheProvider { get; }
///
- /// Define the number of seconds to cache objects for by default
+ /// Defines the value for .
///
CacheDefaults DefaultCachePolicy { get; }
+
+ ///
+ /// Tries to add an item to the cache.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// Object that will be cached.
+ /// Instance of that can be used to configure behavior of the cached item.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
void Add(string key, T item, MemoryCacheEntryOptions policy);
+
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A cached object
+ /// Thrown when is null.
+ /// Thrown when is empty or whitespace.
T Get(string key);
+
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
Task GetAsync(string key);
+
+ ///
+ /// Tries to synchronously retrieve the object associated with the provided key if present.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// Instance that will capture the cached object value.
+ /// True if the item was found.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
bool TryGetValue(string key, out T value);
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// A cached object or null.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
T GetOrAdd(string key, Func addItemFactory);
+
+ ///
+ /// Tries to synchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// Instance of that can be used to configure behavior of the cached item.
+ /// A cached object or null.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
T GetOrAdd(string key, Func addItemFactory, MemoryCacheEntryOptions policy);
+
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
+ ///
+ /// Method will bubble up any exception that may be thrown by delegate.
+ ///
Task GetOrAddAsync(string key, Func> addItemFactory);
+
+ ///
+ /// Tries to asynchronously retrieve an object associated with the provided key.
+ ///
+ /// Item type.
+ /// Cache key.
+ /// A method that will be executed to add an item to the cache.
+ /// Instance of that can be used to configure behavior of the cached item.
+ /// A task with the cached object.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
Task GetOrAddAsync(string key, Func> addItemFactory, MemoryCacheEntryOptions policy);
+
+ ///
+ /// Tries to remove the object associated with the provided key.
+ ///
+ /// Cache key.
+ ///
+ /// Thrown when is null.
+ ///
+ ///
+ /// Thrown when is empty or whitespace.
+ ///
void Remove(string key);
}
}
\ No newline at end of file