DefaultTypedCache.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. using System.Collections;
  2. using EasyCaching.Core;
  3. using Microsoft.Extensions.DependencyInjection;
  4. using Microsoft.Extensions.Options;
  5. using XF.Domain.Cache;
  6. namespace Hotline.EasyCaching
  7. {
  8. public class DefaultTypedCache<TValue> : ITypedCache<TValue>
  9. {
  10. private readonly IServiceScopeFactory _serviceScopeFactory;
  11. private readonly IHybridCachingProvider _hybridCaching;
  12. private readonly IRedisCachingProvider _redisCaching;
  13. private readonly IEasyCachingProvider _caching;
  14. private readonly TimeSpan _expirationDefault = TimeSpan.FromDays(1);
  15. public DefaultTypedCache(
  16. IServiceScopeFactory serviceScopeFactory,
  17. IHybridCachingProvider hybridCaching,
  18. IRedisCachingProvider redisCaching,
  19. IEasyCachingProvider caching
  20. )
  21. {
  22. _serviceScopeFactory = serviceScopeFactory;
  23. _hybridCaching = hybridCaching;
  24. _redisCaching = redisCaching;
  25. _caching = caching;
  26. }
  27. public void Set(string key, TValue value, TimeSpan? expiration = null) =>
  28. _hybridCaching.Set(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
  29. public async Task SetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
  30. await _hybridCaching.SetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
  31. public TValue? Get(string key) => _hybridCaching.Get<TValue>(CombinePrefix(key)).Value;
  32. public async Task<TValue> GetAsync(string key, CancellationToken cancellationToken) =>
  33. (await _hybridCaching.GetAsync<TValue>(CombinePrefix(key), cancellationToken)).Value;
  34. public void Remove(string key) => _hybridCaching.Remove(CombinePrefix(key));
  35. public async Task RemoveAsync(string key, CancellationToken cancellationToken) =>
  36. await _hybridCaching.RemoveAsync(CombinePrefix(key), cancellationToken);
  37. public bool Exists(string key) => _hybridCaching.Exists(CombinePrefix(key));
  38. public async Task<bool> ExistsAsync(string key, CancellationToken cancellationToken) =>
  39. await _hybridCaching.ExistsAsync(CombinePrefix(key), cancellationToken);
  40. public bool TrySet(string key, TValue value, TimeSpan? expiration = null) =>
  41. _hybridCaching.TrySet(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
  42. public async Task<bool> TrySetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
  43. await _hybridCaching.TrySetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
  44. public void SetAll(IDictionary<string, TValue> values, TimeSpan? expiration = null)
  45. {
  46. var newDic = new Dictionary<string, TValue>();
  47. foreach (var kvp in values)
  48. {
  49. newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
  50. }
  51. _hybridCaching.SetAll(newDic, expiration.GetValueOrDefault(_expirationDefault));
  52. }
  53. public async Task SetAllAsync(IDictionary<string, TValue> values, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
  54. {
  55. var newDic = new Dictionary<string, TValue>();
  56. foreach (var kvp in values)
  57. {
  58. newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
  59. }
  60. await _hybridCaching.SetAllAsync(newDic, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
  61. }
  62. public IDictionary<string, TValue> GetByPrefix()
  63. {
  64. var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
  65. if (cacheValueDic == null) return new Dictionary<string, TValue>();
  66. return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
  67. }
  68. public async Task<IDictionary<string, TValue>> GetByPrefixAsync(CancellationToken cancellationToken)
  69. {
  70. var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
  71. if (cacheValueDic == null) return new Dictionary<string, TValue>();
  72. return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
  73. }
  74. public IReadOnlyList<TValue> GetListByPrefix()
  75. {
  76. var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
  77. if (cacheValueDic == null) return new List<TValue>();
  78. return cacheValueDic.Values.Select(d => d.Value).ToList();
  79. }
  80. public async Task<IReadOnlyList<TValue>> GetListByPrefixAsync(CancellationToken cancellationToken)
  81. {
  82. var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
  83. if (cacheValueDic == null) return new List<TValue>();
  84. return cacheValueDic.Values.Select(d => d.Value).ToList();
  85. }
  86. /// <summary>
  87. /// get the cache value, if cache not exists, set the value to cache then return
  88. /// </summary>
  89. public TValue GetOrSet(string key, TValue value, TimeSpan? expiration = null)
  90. {
  91. var cacheValue = Get(key);
  92. if (cacheValue != null) return cacheValue;
  93. Set(key, value, expiration);
  94. return value;
  95. }
  96. public TValue? GetOrSet(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null)
  97. {
  98. var cacheValue = Get(key);
  99. if (cacheValue != null) return cacheValue;
  100. var value = valueFactory.Invoke(key);
  101. if (value == null) return default;
  102. Set(key, value, expiration);
  103. return value;
  104. }
  105. public async Task<TValue> GetOrSetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
  106. {
  107. var cacheValue = await GetAsync(key, cancellationToken);
  108. if (cacheValue != null) return cacheValue;
  109. await SetAsync(key, value, expiration, cancellationToken);
  110. return value;
  111. }
  112. public async Task<TValue?> GetOrSetAsync(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
  113. {
  114. var cacheValue = await GetAsync(key, cancellationToken);
  115. if (cacheValue != null) return cacheValue;
  116. var value = valueFactory.Invoke(key);
  117. if (value == null) return default;
  118. await SetAsync(key, value, expiration, cancellationToken);
  119. return value;
  120. }
  121. public string CombinePrefix(string key) => CreateRegion() + key;
  122. private string CreateRegion()
  123. {
  124. var valueType = typeof(TValue);
  125. //if (valueType.IsGenericType)
  126. //{
  127. // throw new NotSupportedException("TCachedValue can not be a Generic Type");
  128. //}
  129. //if (valueType == typeof(string))
  130. //{
  131. // throw new NotSupportedException("TCachedValue can not be a string");
  132. //}
  133. if (string.IsNullOrWhiteSpace(valueType.FullName))
  134. {
  135. throw new NotSupportedException("FullName of type TCachedValue can not be null");
  136. }
  137. using var scope = _serviceScopeFactory.CreateScope();
  138. var options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<CacheOptions>>();
  139. var prefix = options.Value.Prefix;
  140. //if (valueType.TryGetCustomAttribute<CacheAttribute>(false, out var attr))
  141. //{
  142. // if (!string.IsNullOrEmpty(attr.Region))
  143. // {
  144. // return attr.Region;
  145. // }
  146. //}
  147. string region;
  148. if (valueType.IsGenericType && valueType.GetInterfaces().Any(d => d == typeof(IEnumerable)))
  149. {
  150. region = "Collection";
  151. }
  152. else
  153. {
  154. region = valueType.FullName.Replace('.', ':');
  155. }
  156. if (!string.IsNullOrEmpty(prefix))
  157. {
  158. prefix += ':';
  159. if (!region.StartsWith(prefix))
  160. {
  161. region = prefix + region;
  162. }
  163. }
  164. return region;
  165. }
  166. }
  167. }