public void CacheEntryDependencies() { var cts = new CancellationTokenSource(); var pause = new ManualResetEvent(false); using (var cacheLink = _memoryCache.CreateLinkingScope()) { _memoryCache.Set("master key", "some value", new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(cts.Token))); _memoryCache.Set(_cacheKey, _cacheItem, new MemoryCacheEntryOptions() .AddEntryLink(cacheLink) .RegisterPostEvictionCallback( (key, value, reason, substate) => { _result = $"'{key}':'{value}' was evicted because: {reason}"; pause.Set(); } )); } // trigger the token to expire the master item cts.Cancel(); Assert.True(pause.WaitOne(500)); Assert.Equal("'key':'value' was evicted because: TokenExpired", _result); }
/// <inheritdoc /> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { TagHelperContent result = null; if (Enabled) { var key = GenerateKey(context); if (!MemoryCache.TryGetValue(key, out result)) { // Create an entry link scope and flow it so that any triggers related to the cache entries // created within this scope get copied to this scope. using (var link = MemoryCache.CreateLinkingScope()) { result = await context.GetChildContentAsync(); MemoryCache.Set(key, result, GetMemoryCacheEntryOptions(link)); } } } // Clear the contents of the "cache" element since we don't want to render it. output.SuppressOutput(); if (Enabled) { output.Content.SetContent(result); } else { result = await context.GetChildContentAsync(); output.Content.SetContent(result); } }
/// <inheritdoc /> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } IHtmlContent result = null; if (Enabled) { var key = GenerateKey(context); if (!MemoryCache.TryGetValue(key, out result)) { // Create an entry link scope and flow it so that any tokens related to the cache entries // created within this scope get copied to this scope. using (var link = MemoryCache.CreateLinkingScope()) { var content = await output.GetChildContentAsync(); var stringBuilder = new StringBuilder(); using (var writer = new StringWriter(stringBuilder)) { content.WriteTo(writer, HtmlEncoder); } result = new StringBuilderHtmlContent(stringBuilder); MemoryCache.Set(key, result, GetMemoryCacheEntryOptions(link)); } } } // Clear the contents of the "cache" element since we don't want to render it. output.SuppressOutput(); if (Enabled) { output.Content.SetContent(result); } else { result = await output.GetChildContentAsync(); output.Content.SetContent(result); } }
public void Main() { IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); object result; string key = "Key"; object newObject = new object(); object state = new object(); // Basic CRUD operations: // Create / Overwrite result = cache.Set(key, newObject); result = cache.Set(key, new object()); // Retrieve, null if not found result = cache.Get(key); // Retrieve bool found = cache.TryGetValue(key, out result); // Delete cache.Remove(key); // Cache entry configuration: // Stays in the cache as long as possible result = cache.Set( key, new object(), new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.NeverRemove)); // Automatically remove if not accessed in the given time result = cache.Set( key, new object(), new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(5))); // Automatically remove at a certain time result = cache.Set( key, new object(), new MemoryCacheEntryOptions().SetAbsoluteExpiration(DateTimeOffset.UtcNow.AddDays(2))); // Automatically remove at a certain time, which is relative to UTC now result = cache.Set( key, new object(), new MemoryCacheEntryOptions().SetAbsoluteExpiration(relative: TimeSpan.FromMinutes(10))); // Automatically remove if not accessed in the given time // Automatically remove at a certain time (if it lives that long) result = cache.Set( key, new object(), new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(5)) .SetAbsoluteExpiration(DateTimeOffset.UtcNow.AddDays(2))); // Callback when evicted var options = new MemoryCacheEntryOptions() .RegisterPostEvictionCallback( (echoKey, value, reason, substate) => { Console.WriteLine(echoKey + ": '" + value + "' was evicted due to " + reason); }); result = cache.Set(key, new object(), options); // Remove on token expiration var cts = new CancellationTokenSource(); options = new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(cts.Token)) .RegisterPostEvictionCallback( (echoKey, value, reason, substate) => { Console.WriteLine(echoKey + ": '" + value + "' was evicted due to " + reason); }); result = cache.Set(key, new object(), options); // Fire the token to see the registered callback being invoked cts.Cancel(); // Expire an entry if the dependent entry expires using (var link = cache.CreateLinkingScope()) { cts = new CancellationTokenSource(); cache.Set("key1", "value1", new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(cts.Token))); // expire this entry if the entry with key "key1" expires. cache.Set("key2", "value2", new MemoryCacheEntryOptions() .AddEntryLink(link) .RegisterPostEvictionCallback( (echoKey, value, reason, substate) => { Console.WriteLine(echoKey + ": '" + value + "' was evicted due to " + reason); })); } // Fire the token to see the registered callback being invoked cts.Cancel(); }
/// <inheritdoc /> public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } IHtmlContent content = null; if (Enabled) { var key = GenerateKey(context); MemoryCacheEntryOptions options; while (content == null) { Task <IHtmlContent> result = null; if (!MemoryCache.TryGetValue(key, out result)) { var tokenSource = new CancellationTokenSource(); // Create an entry link scope and flow it so that any tokens related to the cache entries // created within this scope get copied to this scope. options = GetMemoryCacheEntryOptions(); options.AddExpirationToken(new CancellationChangeToken(tokenSource.Token)); var tcs = new TaskCompletionSource <IHtmlContent>(); MemoryCache.Set(key, tcs.Task, options); try { using (var link = MemoryCache.CreateLinkingScope()) { result = ProcessContentAsync(output); content = await result; options.AddEntryLink(link); } // The entry is set instead of assigning a value to the // task so that the expiration options are are not impacted // by the time it took to compute it. MemoryCache.Set(key, result, options); } catch { // Remove the worker task from the cache in case it can't complete. tokenSource.Cancel(); throw; } finally { // If an exception occurs, ensure the other awaiters // render the output by themselves. tcs.SetResult(null); } } else { // There is either some value already cached (as a Task) // or a worker processing the output. In the case of a worker, // the result will be null, and the request will try to acquire // the result from memory another time. content = await result; } } } else { content = await output.GetChildContentAsync(); } // Clear the contents of the "cache" element since we don't want to render it. output.SuppressOutput(); output.Content.SetContent(content); }