public async Task DisplayingAsync(ShapeDisplayContext context) { // TODO: replace with configurable UI var debugMode = false; // The shape has cache settings and no content yet if (context.ShapeMetadata.IsCached && context.ChildContent == null) { var cacheContext = context.ShapeMetadata.Cache(); _cacheScopeManager.EnterScope(cacheContext); _openScopes[cacheContext.CacheId] = cacheContext; var cachedContent = await _dynamicCacheService.GetCachedValueAsync(cacheContext); if (cachedContent != null) { // The contents of this shape was found in the cache. // Add the cacheContext to _cached so that we don't try to cache the content again in the DisplayedAsync method. _cached[cacheContext.CacheId] = cacheContext; context.ChildContent = new HtmlString(cachedContent); } else if (debugMode) { context.ShapeMetadata.Wrappers.Add("CachedShapeWrapper"); } } }
private async Task <DateTime> GetOrCreateDynamicCachedDateTimeAsync(CacheContext cacheContext) { // Now that we have a cache context we try to acquire the object. The objects always need to be strings. var cachedDateTimeText = await _dynamicCacheService.GetCachedValueAsync(cacheContext); // If the date time text is not null then parse it to DateTime otherwise use the ILocalClock service to set // it to the current date. var cachedDateTime = cachedDateTimeText != null? DateTime.Parse(cachedDateTimeText, CultureInfo.InvariantCulture) : (await _localClock.LocalNowAsync).DateTime; // If the date time text is null (meaning it wasn't cached) cache the DateTime object (which in this case // is the current date). if (cachedDateTimeText == null) { await _dynamicCacheService.SetCachedValueAsync( cacheContext, cachedDateTime.ToString(CultureInfo.InvariantCulture)); } return(cachedDateTime); }
public async Task <IHtmlContent> ProcessContentAsync(TagHelperOutput output, CacheContext cacheContext) { IHtmlContent content = null; while (content == null) { Task <IHtmlContent> result; // Is there any request already processing the value? if (!_dynamicCacheTagHelperService.Workers.TryGetValue(CacheId, out result)) { // There is a small race condition here between TryGetValue and TryAdd that might cause the // content to be computed more than once. We don't care about this race as the probability of // happening is very small and the impact is not critical. var tcs = new TaskCompletionSource <IHtmlContent>(); _dynamicCacheTagHelperService.Workers.TryAdd(CacheId, tcs.Task); try { var value = await _dynamicCacheService.GetCachedValueAsync(cacheContext); if (value == null) { // The value is not cached, we need to render the tag helper output var processedContent = await output.GetChildContentAsync(); var stringBuilder = new StringBuilder(); using (var writer = new StringWriter(stringBuilder)) { processedContent.WriteTo(writer, HtmlEncoder); } var formattingContext = new DistributedCacheTagHelperFormattingContext { Html = new HtmlString(stringBuilder.ToString()) }; await _dynamicCacheService.SetCachedValueAsync(cacheContext, formattingContext.Html.ToString()); content = formattingContext.Html; } else { content = new HtmlString(value); } } catch { content = null; throw; } finally { // Remove the worker task before setting the result. // If the result is null, other threads would potentially // acquire it otherwise. _dynamicCacheTagHelperService.Workers.TryRemove(CacheId, out result); // Notify all other awaiters to render the content tcs.TrySetResult(content); } } else { content = await result; } } return(content); }
public async Task <IHtmlContent> ProcessContentAsync(TagHelperOutput output, CacheContext cacheContext) { IHtmlContent content = null; while (content == null) { Task <IHtmlContent> result; // Is there any request already processing the value? if (!_dynamicCacheTagHelperService.Workers.TryGetValue(CacheId, out result)) { // There is a small race condition here between TryGetValue and TryAdd that might cause the // content to be computed more than once. We don't care about this race as the probability of // happening is very small and the impact is not critical. var tcs = new TaskCompletionSource <IHtmlContent>(); _dynamicCacheTagHelperService.Workers.TryAdd(CacheId, tcs.Task); try { var value = await _dynamicCacheService.GetCachedValueAsync(cacheContext); if (value == null) { // The value is not cached, we need to render the tag helper output var processedContent = await output.GetChildContentAsync(); using (var sb = StringBuilderPool.GetInstance()) { using (var writer = new StringWriter(sb.Builder)) { // Write the start of a cache debug block. if (_cacheOptions.DebugMode) { // No need to optimize this code as it will be used for debugging purpose. writer.WriteLine(); writer.WriteLine($"<!-- CACHE BLOCK: {cacheContext.CacheId} ({Guid.NewGuid()})"); writer.WriteLine($" VARY BY: {String.Join(", ", cacheContext.Contexts)}"); writer.WriteLine($" DEPENDENCIES: {String.Join(", ", cacheContext.Tags)}"); writer.WriteLine($" EXPIRES ON: {cacheContext.ExpiresOn}"); writer.WriteLine($" EXPIRES AFTER: {cacheContext.ExpiresAfter}"); writer.WriteLine($" EXPIRES SLIDING: {cacheContext.ExpiresSliding}"); writer.WriteLine("-->"); } // Always write the content regardless of debug mode. processedContent.WriteTo(writer, HtmlEncoder); // Write the end of a cache debug block. if (_cacheOptions.DebugMode) { writer.WriteLine(); writer.WriteLine($"<!-- END CACHE BLOCK: {cacheContext.CacheId} -->"); } await writer.FlushAsync(); } var html = sb.Builder.ToString(); var formattingContext = new DistributedCacheTagHelperFormattingContext { Html = new HtmlString(html) }; await _dynamicCacheService.SetCachedValueAsync(cacheContext, html); content = formattingContext.Html; } } else { content = new HtmlString(value); } } catch { content = null; throw; } finally { // Remove the worker task before setting the result. // If the result is null, other threads would potentially // acquire it otherwise. _dynamicCacheTagHelperService.Workers.TryRemove(CacheId, out result); // Notify all other awaiters to render the content tcs.TrySetResult(content); } } else { content = await result; } } return(content); }