|
@@ -0,0 +1,204 @@
|
|
|
+using System.Collections;
|
|
|
+using EasyCaching.Core;
|
|
|
+using Microsoft.Extensions.DependencyInjection;
|
|
|
+using Microsoft.Extensions.Options;
|
|
|
+using XF.Domain.Cache;
|
|
|
+
|
|
|
+namespace CallCenter.Caching
|
|
|
+{
|
|
|
+ public class DefaultTypedCache<TValue> : ITypedCache<TValue>
|
|
|
+ {
|
|
|
+ private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
|
+ private readonly IHybridCachingProvider _hybridCaching;
|
|
|
+ private readonly IRedisCachingProvider _redisCaching;
|
|
|
+ private readonly IEasyCachingProvider _caching;
|
|
|
+ private readonly TimeSpan _expirationDefault = TimeSpan.FromDays(1);
|
|
|
+
|
|
|
+ public DefaultTypedCache(
|
|
|
+ IServiceScopeFactory serviceScopeFactory,
|
|
|
+ IHybridCachingProvider hybridCaching,
|
|
|
+ IRedisCachingProvider redisCaching,
|
|
|
+ IEasyCachingProvider caching)
|
|
|
+ {
|
|
|
+ _serviceScopeFactory = serviceScopeFactory;
|
|
|
+ _hybridCaching = hybridCaching;
|
|
|
+ _redisCaching = redisCaching;
|
|
|
+ _caching = caching;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Set(string key, TValue value, TimeSpan? expiration = null) =>
|
|
|
+ _hybridCaching.Set(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
|
|
|
+
|
|
|
+ public async Task SetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
|
|
|
+ await _hybridCaching.SetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
|
|
|
+
|
|
|
+ public TValue? Get(string key) => _hybridCaching.Get<TValue>(CombinePrefix(key)).Value;
|
|
|
+
|
|
|
+ public async Task<TValue> GetAsync(string key, CancellationToken cancellationToken) =>
|
|
|
+ (await _hybridCaching.GetAsync<TValue>(CombinePrefix(key), cancellationToken)).Value;
|
|
|
+
|
|
|
+ public void Remove(string key) => _hybridCaching.Remove(CombinePrefix(key));
|
|
|
+
|
|
|
+ public async Task RemoveAsync(string key, CancellationToken cancellationToken) =>
|
|
|
+ await _hybridCaching.RemoveAsync(CombinePrefix(key), cancellationToken);
|
|
|
+
|
|
|
+ public bool Exists(string key) => _hybridCaching.Exists(CombinePrefix(key));
|
|
|
+
|
|
|
+ public async Task<bool> ExistsAsync(string key, CancellationToken cancellationToken) =>
|
|
|
+ await _hybridCaching.ExistsAsync(CombinePrefix(key), cancellationToken);
|
|
|
+
|
|
|
+ public bool TrySet(string key, TValue value, TimeSpan? expiration = null) =>
|
|
|
+ _hybridCaching.TrySet(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
|
|
|
+
|
|
|
+ public async Task<bool> TrySetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
|
|
|
+ await _hybridCaching.TrySetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
|
|
|
+
|
|
|
+ public void SetAll(IDictionary<string, TValue> values, TimeSpan? expiration = null)
|
|
|
+ {
|
|
|
+ var newDic = new Dictionary<string, TValue>();
|
|
|
+ foreach (var kvp in values)
|
|
|
+ {
|
|
|
+ newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
|
|
|
+ }
|
|
|
+ _hybridCaching.SetAll(newDic, expiration.GetValueOrDefault(_expirationDefault));
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task SetAllAsync(IDictionary<string, TValue> values, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var newDic = new Dictionary<string, TValue>();
|
|
|
+ foreach (var kvp in values)
|
|
|
+ {
|
|
|
+ newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
|
|
|
+ }
|
|
|
+
|
|
|
+ await _hybridCaching.SetAllAsync(newDic, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ public IDictionary<string, TValue> GetByPrefix()
|
|
|
+ {
|
|
|
+ var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
|
|
|
+ if (cacheValueDic == null) return new Dictionary<string, TValue>();
|
|
|
+ return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<IDictionary<string, TValue>> GetByPrefixAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
|
|
|
+ if (cacheValueDic == null) return new Dictionary<string, TValue>();
|
|
|
+ return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public IReadOnlyList<TValue> GetListByPrefix()
|
|
|
+ {
|
|
|
+ var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
|
|
|
+ if (cacheValueDic == null) return new List<TValue>();
|
|
|
+ return cacheValueDic.Values.Select(d => d.Value).ToList();
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<IReadOnlyList<TValue>> GetListByPrefixAsync(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
|
|
|
+ if (cacheValueDic == null) return new List<TValue>();
|
|
|
+ return cacheValueDic.Values.Select(d => d.Value).ToList();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// get the cache value, if cache not exists, set the value to cache then return
|
|
|
+ /// </summary>
|
|
|
+ public TValue GetOrSet(string key, TValue value, TimeSpan? expiration = null)
|
|
|
+ {
|
|
|
+ var cacheValue = Get(key);
|
|
|
+ if (cacheValue != null) return cacheValue;
|
|
|
+
|
|
|
+ Set(key, value, expiration);
|
|
|
+ return value;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public TValue? GetOrSet(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null)
|
|
|
+ {
|
|
|
+ var cacheValue = Get(key);
|
|
|
+ if (cacheValue != null) return cacheValue;
|
|
|
+
|
|
|
+ var value = valueFactory.Invoke(key);
|
|
|
+ if (value == null) return default;
|
|
|
+ Set(key, value, expiration);
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<TValue> GetOrSetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var cacheValue = await GetAsync(key, cancellationToken);
|
|
|
+ if (cacheValue != null) return cacheValue;
|
|
|
+
|
|
|
+ await SetAsync(key, value, expiration, cancellationToken);
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async Task<TValue?> GetOrSetAsync(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
|
|
|
+ {
|
|
|
+ var cacheValue = await GetAsync(key, cancellationToken);
|
|
|
+ if (cacheValue != null) return cacheValue;
|
|
|
+
|
|
|
+ var value = valueFactory.Invoke(key);
|
|
|
+ if (value == null) return default;
|
|
|
+ await SetAsync(key, value, expiration, cancellationToken);
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ public string CombinePrefix(string key) => CreateRegion() + key;
|
|
|
+
|
|
|
+
|
|
|
+ private string CreateRegion()
|
|
|
+ {
|
|
|
+ var valueType = typeof(TValue);
|
|
|
+ //if (valueType.IsGenericType)
|
|
|
+ //{
|
|
|
+ // throw new NotSupportedException("TCachedValue can not be a Generic Type");
|
|
|
+ //}
|
|
|
+
|
|
|
+ //if (valueType == typeof(string))
|
|
|
+ //{
|
|
|
+ // throw new NotSupportedException("TCachedValue can not be a string");
|
|
|
+ //}
|
|
|
+
|
|
|
+ if (string.IsNullOrWhiteSpace(valueType.FullName))
|
|
|
+ {
|
|
|
+ throw new NotSupportedException("FullName of type TCachedValue can not be null");
|
|
|
+ }
|
|
|
+
|
|
|
+ using var scope = _serviceScopeFactory.CreateScope();
|
|
|
+ var options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<CacheOptions>>();
|
|
|
+ var prefix = options.Value.Prefix;
|
|
|
+
|
|
|
+ //if (valueType.TryGetCustomAttribute<CacheAttribute>(false, out var attr))
|
|
|
+ //{
|
|
|
+ // if (!string.IsNullOrEmpty(attr.Region))
|
|
|
+ // {
|
|
|
+ // return attr.Region;
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+
|
|
|
+ string region;
|
|
|
+
|
|
|
+ if (valueType.IsGenericType && valueType.GetInterfaces().Any(d => d == typeof(IEnumerable)))
|
|
|
+ {
|
|
|
+ region = "Collection";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ region = valueType.FullName.Replace('.', ':');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(prefix))
|
|
|
+ {
|
|
|
+ prefix += ':';
|
|
|
+ if (!region.StartsWith(prefix))
|
|
|
+ {
|
|
|
+ region = prefix + region;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return region;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|