/// <summary> /// Gets a data from cache if possible, otherwise from original source then sets cache. /// </summary> /// <typeparam name="T">Targeted data type.</typeparam> /// <param name="cache">Cache provider.</param> /// <param name="cacheKey">Cache key.</param> /// <param name="options">Cache options.</param> /// <param name="getWithoutCacheDelegate">Delegate to retrieve data from the original source.</param> /// <returns>The data.</returns> internal static async Task <T> GetOrSetFromCache <T>( this IDistributedCache cache, string cacheKey, DistributedCacheEntryOptions options, Func <Task <T> > getWithoutCacheDelegate) where T : class, new() { T datas = await cache.GetFromCache <T>(cacheKey).ConfigureAwait(false); if (datas == null) { SemaphoreSlim semaphore = _semaphores.GetOrAdd(cacheKey, new SemaphoreSlim(1, 1)); await semaphore.WaitAsync().ConfigureAwait(false); try { datas = await cache.GetFromCache <T>(cacheKey).ConfigureAwait(false); if (datas == null) { datas = await getWithoutCacheDelegate().ConfigureAwait(false); if (datas != null) { await cache.SetAsync(cacheKey, Serialize(datas), options).ConfigureAwait(false); } } } finally { semaphore.Release(); } } return(datas); }