Skip to content

Instantly share code, notes, and snippets.

@jelical
Last active April 29, 2019 01:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jelical/17a3e1ef03af2901f4eb6866291239c6 to your computer and use it in GitHub Desktop.
Save jelical/17a3e1ef03af2901f4eb6866291239c6 to your computer and use it in GitHub Desktop.
//Inspired by https://gist.github.com/ttrider/a5ae8fc86ccfe6a4243f4481a5858a80
//Thread safe, Lazy based wrapper for IMemoryCache. Supports multiple IMemoryCache instances
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Caching.Memory
{
/// <summary>
/// Thread safe version of GetOrCreate(Async) using Lazy<T>
/// </summary>
public static class LazyCacheExtensions
{
private class LazySlimAsync<T,W> : Lazy<Task<T>>
{
public LazySlimAsync(W key, Func<W, Task<T>> factory) :
base(async () => await factory(key).ConfigureAwait(false)) { }
public TaskAwaiter<T> GetAwaiter() => Value.GetAwaiter();
public T Result => Value.Result;
public static implicit operator Task<T>(LazySlimAsync<T,W> @this) => @this.Value;
}
private static readonly ConcurrentDictionary<ValueTuple<object, IMemoryCache>, LazySlimAsync<object,ICacheEntry>> Tasks =
new ConcurrentDictionary<ValueTuple<object, IMemoryCache>, LazySlimAsync<object,ICacheEntry>>();
public static TItem GetOrCreateLazy<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, TItem> factoryMethod)
{
return cache.GetOrCreateLazyAsync(key, (a) => Task.FromResult(factoryMethod(a))).Result;
}
public static async Task<TItem> GetOrCreateLazyAsync<TItem>(this IMemoryCache cache, object key, Func<ICacheEntry, Task<TItem>> factoryMethod)
{
if (!cache.TryGetValue(key, out object result))
{
var asyncLazy = Tasks.GetOrAdd((key, cache), (k) =>
{
return new LazySlimAsync<object,ICacheEntry>(cache.CreateEntry(k.Item1), async (kk) =>
{
object res = null;
try
{
if(!cache.TryGetValue(key, out res))
{
kk.SetValue(res = await factoryMethod(kk)).Dispose();
}
}
finally
{
Tasks.TryRemove(k, out LazySlimAsync<object,ICacheEntry> oldAsync);
}
return res;
});
});
return (TItem)await asyncLazy;
}
return (TItem)result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment