Advertisement
Guest User

Untitled

a guest
Feb 18th, 2020
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.28 KB | None | 0 0
  1. using Microsoft.Extensions.Caching.Memory;
  2. using Microsoft.Extensions.Primitives;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7.  
  8. namespace MFL.Caching
  9. {
  10. public static class CacheConfigurator
  11. {
  12. public static void ConfigureCacheAccess(this IServiceCollection serviceList)
  13. {
  14. serviceList.AddMemoryCache();
  15. serviceList.TryAddSingleton<ICacheService, CacheService>();
  16. }
  17. }
  18.  
  19. public interface ICacheService : IDisposable
  20. {
  21. void ResetCache();
  22. Task<TItem> GetOrCreateAsync<TItem>(string prefix, object key, Func<ICacheEntry, Task<TItem>> factory, CancellationToken token);
  23. TItem GetOrCreate<TItem>(string prefix, object key, Func<ICacheEntry, TItem> factory, CancellationToken token);
  24. }
  25.  
  26.  
  27.  
  28. internal class CacheService : ICacheService
  29. {
  30. private static readonly ConcurrentDictionary<string, SemaphoreSlim> SemaphoreDictionary = new ConcurrentDictionary<string, SemaphoreSlim>();
  31.  
  32. public CancellationTokenSource ResetCacheToken { get; set; } = new CancellationTokenSource();
  33.  
  34. private readonly IMemoryCache _cache;
  35.  
  36. public CacheService(IMemoryCache cache) => _cache = cache;
  37.  
  38. public void Dispose() => ResetCacheToken.Dispose();
  39.  
  40. public async Task<TItem> GetOrCreateAsync<TItem>(string prefix, object key, Func<ICacheEntry, Task<TItem>> factory, CancellationToken token)
  41. {
  42. var svcKey = $"{prefix}_{key}";
  43.  
  44. //Here the semaphore avoids running 2 factories in case we are requesting the same key at the same time.
  45. //It's basically an implementation of the "lock" mechanism but for asynchronous code
  46. var semaphore = SemaphoreDictionary.GetOrAdd(svcKey, new SemaphoreSlim(1));
  47. await semaphore.WaitAsync(token).ConfigureAwait(false);
  48. try
  49. {
  50. if (_cache.TryGetValue(svcKey, out var value))
  51. return (TItem)value;
  52.  
  53. var entry = CreateCancellableCacheEntry(svcKey);
  54. var createdVal = await factory(entry).ConfigureAwait(false);
  55.  
  56. if (createdVal == null)
  57. throw new NullReferenceException();
  58.  
  59. SetEntryValue(entry, createdVal);
  60. return createdVal;
  61. }
  62. finally
  63. {
  64. semaphore.Release();
  65. }
  66. }
  67.  
  68. public TItem GetOrCreate<TItem>(string prefix, object key, Func<ICacheEntry, TItem> factory, CancellationToken token)
  69. {
  70. var svcKey = $"{prefix}_{key}";
  71.  
  72. //Here the semaphore avoids running 2 factories in case we are requesting the same key at the same time.
  73. //It's basically an implementation of the "lock" mechanism but for asynchronous code
  74. var semaphore = SemaphoreDictionary.GetOrAdd(svcKey, new SemaphoreSlim(1));
  75. semaphore.Wait(token);
  76. try
  77. {
  78. if (_cache.TryGetValue(svcKey, out var value)) return (TItem)value;
  79.  
  80. var entry = CreateCancellableCacheEntry(svcKey);
  81. var createdVal = factory(entry);
  82.  
  83. if (createdVal == null)
  84. throw new NullReferenceException();
  85.  
  86. SetEntryValue(entry, createdVal);
  87. return createdVal;
  88. }
  89. finally
  90. {
  91. semaphore.Release();
  92. }
  93. }
  94.  
  95. public void ResetCache()
  96. {
  97. if (ResetCacheToken?.IsCancellationRequested == false && ResetCacheToken.Token.CanBeCanceled)
  98. {
  99. ResetCacheToken.Cancel();
  100. ResetCacheToken.Dispose();
  101. }
  102.  
  103. ResetCacheToken = new CancellationTokenSource();
  104. }
  105.  
  106. private static void SetEntryValue(ICacheEntry entry, object value)
  107. {
  108. entry.SetValue(value);
  109. entry.Dispose();
  110. }
  111.  
  112. private ICacheEntry CreateCancellableCacheEntry(object key)
  113. {
  114. var entry = _cache.CreateEntry(key);
  115. entry.AddExpirationToken(new CancellationChangeToken(ResetCacheToken.Token));
  116.  
  117. return entry;
  118. }
  119. }
  120. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement