using System.Collections; using EasyCaching.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using XF.Domain.Cache; namespace XF.EasyCaching { public class DefaultTypedCache : ITypedCache { 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(CombinePrefix(key)).Value; public async Task GetAsync(string key, CancellationToken cancellationToken) => (await _hybridCaching.GetAsync(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 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 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 values, TimeSpan? expiration = null) { var newDic = new Dictionary(); foreach (var kvp in values) { newDic.Add(CombinePrefix(kvp.Key), kvp.Value); } _hybridCaching.SetAll(newDic, expiration.GetValueOrDefault(_expirationDefault)); } public async Task SetAllAsync(IDictionary values, TimeSpan? expiration = null, CancellationToken cancellationToken = default) { var newDic = new Dictionary(); foreach (var kvp in values) { newDic.Add(CombinePrefix(kvp.Key), kvp.Value); } await _hybridCaching.SetAllAsync(newDic, expiration.GetValueOrDefault(_expirationDefault), cancellationToken); } public IDictionary GetByPrefix() { var cacheValueDic = _caching.GetByPrefix(CreateRegion()); if (cacheValueDic == null) return new Dictionary(); return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value); } public async Task> GetByPrefixAsync(CancellationToken cancellationToken) { var cacheValueDic = await _caching.GetByPrefixAsync(CreateRegion(), cancellationToken); if (cacheValueDic == null) return new Dictionary(); return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value); } public IReadOnlyList GetListByPrefix() { var cacheValueDic = _caching.GetByPrefix(CreateRegion()); if (cacheValueDic == null) return new List(); return cacheValueDic.Values.Select(d => d.Value).ToList(); } public async Task> GetListByPrefixAsync(CancellationToken cancellationToken) { var cacheValueDic = await _caching.GetByPrefixAsync(CreateRegion(), cancellationToken); if (cacheValueDic == null) return new List(); return cacheValueDic.Values.Select(d => d.Value).ToList(); } /// /// get the cache value, if cache not exists, set the value to cache then return /// 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 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 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 GetOrSetAsync(string key, Func 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>(); var prefix = options.Value.Prefix; //if (valueType.TryGetCustomAttribute(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; } } }