Last active
April 29, 2019 01:46
-
-
Save jelical/17a3e1ef03af2901f4eb6866291239c6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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