(资料图片)
1. 缓存模块源码解析个人觉得 ABP 分布式缓存模块有三个值得关注的核心点。首先是 AbpRedisCache 类继承了微软原生的 RedisCache,并 通过反射的方式获取RedisCache的私有方法对 RedisCache 进行扩展,实现了 ABP 分布式缓存中的批量操作方法。
为什么要这么做呢?因为基于 Redis 缓存的批量操作需要使用到 StackExchange.Redis 原生的SDK,而且在进行操作前总需要先进行 Redis 连接,而相应的方法和属性原生的 RedisCache 中都有,但是都是私有(private)的,在继承类中也无法使用,所以使用反射的方式提取出相应的方法和属性,以便在继承类中复用。这也是对于类功能进行继承扩展的时候的一种很有用的方式。
第二点是 ABP 缓存模块通过 IDistributedCache
而最常使用的 IDistributedCache
接口,以前其实现类是直接继承 DistributedCache
最后一个是缓存的事务性,通过工作单元使缓存和其他事务操作保持原子性,避免缓存已经更新而其他事务失败回滚导致数据不一致的问题。实际上就是先不真正地更新缓存,而是将缓存数据通过字典保存在工作单元中,保证一个工作单元内拿到的缓存数据是最新的,同时注册工作单元提交事件,在工作单元正在提交成功的时候才执行真正更新缓存的逻辑。
工作单元相关的内容就后面再在专门的章节讲吧,这部分的内容比较复杂,一时半会比较难讲清。
2. 自己扩展的 IDistributedCacheABP 框架扩展的 IDistributedCache
IDistributedCache
接口进行扩展。内部实现基本一致,主要就是将基于类的泛型,改成基于方法的泛型,并且提供自己的 IDistributedCacheKeyNormalizer 实现类,将缓存键的设置规则交给了缓存存取时进行设置。 代码如下:public interface IWantDistributedCache{/// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The cache item, or null. TCacheItem Get(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Gets multiple cache items with the given keys.////// The returned list contains exactly the same count of items specified in the given keys./// An item in the return list can not be null, but an item in the list has null value/// if the related key not found in the cache./// /// The keys of cached items to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// List of cache items. KeyValuePair[] GetMany(IEnumerable keys,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Gets multiple cache items with the given keys.////// The returned list contains exactly the same count of items specified in the given keys./// An item in the return list can not be null, but an item in the list has null value/// if the related key not found in the cache.////// /// The keys of cached items to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// /// The for the task./// List of cache items. Task[]> GetManyAsync(IEnumerable keys,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;/// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The cache item, or null. Task GetAsync([NotNull] TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;/// /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item/// provided by delegate and returns the provided cache item./// /// The key of cached item to be retrieved from the cache./// The factory delegate is used to provide the cache item when no cache item is found for the given ./// The cache options for the factory delegate./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The cache item. TCacheItem GetOrAdd(TCacheKey key,Func factory,string cacheName,Func optionsFactory = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item/// provided by delegate and returns the provided cache item./// /// The key of cached item to be retrieved from the cache./// The factory delegate is used to provide the cache item when no cache item is found for the given ./// The cache options for the factory delegate./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The cache item. Task GetOrAddAsync([NotNull] TCacheKey key,Func> factory,string cacheName,Func optionsFactory = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;/// /// Sets the cache item value for the provided key./// /// The key of cached item to be retrieved from the cache./// The cache item value to set in the cache./// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.void Set(TCacheKey key,TCacheItem value,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Sets the cache item value for the provided key./// /// The key of cached item to be retrieved from the cache./// The cache item value to set in the cache./// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The indicating that the operation is asynchronous. Task SetAsync([NotNull] TCacheKey key,[NotNull] TCacheItem value,string cacheName,[CanBeNull] DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;/// /// Sets multiple cache items./// Based on the implementation, this can be more efficient than setting multiple items individually./// /// Items to set on the cache/// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.void SetMany(IEnumerable> items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Sets multiple cache items./// Based on the implementation, this can be more efficient than setting multiple items individually./// /// Items to set on the cache/// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The indicating that the operation is asynchronous. Task SetManyAsync(IEnumerable> items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;/// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache.void Refresh(TCacheKey key,string cacheName,bool? hideErrors = null);/// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// The for the task./// The indicating that the operation is asynchronous. Task RefreshAsync(TCacheKey key,string cacheName,bool? hideErrors = null,CancellationToken token = default);/// /// Removes the cache item for given key from cache./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.void Remove(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class;/// /// Removes the cache item for given key from cache./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The indicating that the operation is asynchronous. Task RemoveAsync(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class;}public class WantDistributedCache : IWantDistributedCache{public const string UowCacheName = "WantDistributedCache";public ILogger Logger { get; set; }protected string CacheName { get; set; }protected bool IgnoreMultiTenancy { get; set; }protected IDistributedCache Cache { get; }protected ICancellationTokenProvider CancellationTokenProvider { get; }protected IDistributedCacheSerializer Serializer { get; }protected IDistributedCacheKeyNormalizer KeyNormalizer { get; }protected IHybridServiceScopeFactory ServiceScopeFactory { get; }protected IUnitOfWorkManager UnitOfWorkManager { get; }protected SemaphoreSlim SyncSemaphore { get; }protected DistributedCacheEntryOptions DefaultCacheOptions;private readonly AbpDistributedCacheOptions _distributedCacheOption;public WantDistributedCache(IOptions distributedCacheOption,IDistributedCache cache,ICancellationTokenProvider cancellationTokenProvider,IDistributedCacheSerializer serializer,IDistributedCacheKeyNormalizer keyNormalizer,IHybridServiceScopeFactory serviceScopeFactory,IUnitOfWorkManager unitOfWorkManager){_distributedCacheOption = distributedCacheOption.Value;Cache = cache;CancellationTokenProvider = cancellationTokenProvider;Logger = NullLogger.Instance;Serializer = serializer;KeyNormalizer = keyNormalizer;ServiceScopeFactory = serviceScopeFactory;UnitOfWorkManager = unitOfWorkManager;SyncSemaphore = new SemaphoreSlim(1, 1);SetDefaultOptions();}protected virtual string NormalizeKey(TCacheKey key, string cacheName){return KeyNormalizer.NormalizeKey(new DistributedCacheKeyNormalizeArgs(key.ToString(),cacheName,IgnoreMultiTenancy));}protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions(){foreach (var configure in _distributedCacheOption.CacheConfigurators){var options = configure.Invoke(CacheName);if (options != null){return options;}}return _distributedCacheOption.GlobalCacheEntryOptions;}protected virtual void SetDefaultOptions(){//CacheName = CacheNameAttribute.GetCacheName(typeof(TCacheItem));////IgnoreMultiTenancy//IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);//Configure default cache entry optionsDefaultCacheOptions = GetDefaultCacheEntryOptions();}/// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The cache item, or null. public virtual TCacheItem Get(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;if (ShouldConsiderUow(considerUow)){var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();if (value != null){return value;}}byte[] cachedBytes;try{cachedBytes = Cache.Get(NormalizeKey(key, cacheName));}catch (Exception ex){if (hideErrors == true){HandleException(ex);return null;}throw;}return ToCacheItem(cachedBytes);}public virtual KeyValuePair[] GetMany(IEnumerable keys,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{var keyArray = keys.ToArray();var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;if (cacheSupportsMultipleItems == null){return GetManyFallback(keyArray,cacheName,hideErrors,considerUow);}var notCachedKeys = new List();var cachedValues = new List>();if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();foreach (var key in keyArray){var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();if (value != null){cachedValues.Add(new KeyValuePair(key, value));}}notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();if (!notCachedKeys.Any()){return cachedValues.ToArray();}}hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;byte[][] cachedBytes;var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;try{cachedBytes = cacheSupportsMultipleItems.GetMany(readKeys.Select(key => NormalizeKey(key, cacheName)));}catch (Exception ex){if (hideErrors == true){HandleException(ex);return ToCacheItemsWithDefaultValues(keyArray);}throw;}return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();}protected virtual KeyValuePair[] GetManyFallback(TCacheKey[] keys,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{return keys.Select(key => new KeyValuePair(key,Get(key, cacheName, false, considerUow))).ToArray();}catch (Exception ex){if (hideErrors == true){HandleException(ex);return ToCacheItemsWithDefaultValues(keys);}throw;}}public virtual async Task[]> GetManyAsync(IEnumerable keys,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{var keyArray = keys.ToArray();var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;if (cacheSupportsMultipleItems == null){return await GetManyFallbackAsync(keyArray,cacheName,hideErrors,considerUow,token);}var notCachedKeys = new List();var cachedValues = new List>();if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();foreach (var key in keyArray){var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();if (value != null){cachedValues.Add(new KeyValuePair(key, value));}}notCachedKeys = keyArray.Except(cachedValues.Select(x => x.Key)).ToList();if (!notCachedKeys.Any()){return cachedValues.ToArray();}}hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;byte[][] cachedBytes;var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;try{cachedBytes = await cacheSupportsMultipleItems.GetManyAsync(readKeys.Select(key => NormalizeKey(key, cacheName)),CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return ToCacheItemsWithDefaultValues(keyArray);}throw;}return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();}protected virtual async Task[]> GetManyFallbackAsync(TCacheKey[] keys,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{var result = new List>();foreach (var key in keys){result.Add(new KeyValuePair(key,await GetAsync(key, cacheName, false, considerUow, token: token)));}return result.ToArray();}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return ToCacheItemsWithDefaultValues(keys);}throw;}}/// /// Gets a cache item with the given key. If no cache item is found for the given key then returns null./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The cache item, or null. public virtual async Task GetAsync(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;if (ShouldConsiderUow(considerUow)){var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();if (value != null){return value;}}byte[] cachedBytes;try{cachedBytes = await Cache.GetAsync(NormalizeKey(key, cacheName),CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return null;}throw;}if (cachedBytes == null){return null;}return Serializer.Deserialize(cachedBytes);}/// /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item/// provided by delegate and returns the provided cache item./// /// The key of cached item to be retrieved from the cache./// The factory delegate is used to provide the cache item when no cache item is found for the given ./// The cache options for the factory delegate./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The cache item. public virtual TCacheItem GetOrAdd(TCacheKey key,Func factory,string cacheName,Func optionsFactory = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{var value = Get(key, cacheName, hideErrors, considerUow);if (value != null){return value;}using (SyncSemaphore.Lock()){value = Get(key, cacheName, hideErrors, considerUow);if (value != null){return value;}value = factory();if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out var item)){item.SetValue(value);}else{uowCache.Add(key, new UnitOfWorkCacheItem(value));}}Set(key, value, cacheName, optionsFactory?.Invoke(), hideErrors, considerUow);}return value;}/// /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item/// provided by delegate and returns the provided cache item./// /// The key of cached item to be retrieved from the cache./// The factory delegate is used to provide the cache item when no cache item is found for the given ./// The cache options for the factory delegate./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The cache item. public virtual async Task GetOrAddAsync(TCacheKey key,Func> factory,string cacheName,Func optionsFactory = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{token = CancellationTokenProvider.FallbackToProvider(token);var value = await GetAsync(key, cacheName, hideErrors, considerUow, token);if (value != null){return value;}using (await SyncSemaphore.LockAsync(token)){value = await GetAsync(key, cacheName, hideErrors, considerUow, token);if (value != null){return value;}value = await factory();if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out var item)){item.SetValue(value);}else{uowCache.Add(key, new UnitOfWorkCacheItem(value));}}await SetAsync(key, value, cacheName, optionsFactory?.Invoke(), hideErrors, considerUow, token);}return value;}/// /// Sets the cache item value for the provided key./// /// The key of cached item to be retrieved from the cache./// The cache item value to set in the cache./// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache.public virtual void Set(TCacheKey key,TCacheItem value,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{void SetRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{Cache.Set(NormalizeKey(key, cacheName),Serializer.Serialize(value),options ?? DefaultCacheOptions);}catch (Exception ex){if (hideErrors == true){HandleException(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out _)){uowCache[key].SetValue(value);}else{uowCache.Add(key, new UnitOfWorkCacheItem(value));}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(() =>{SetRealCache();return Task.CompletedTask;});}else{SetRealCache();}}/// /// Sets the cache item value for the provided key./// /// The key of cached item to be retrieved from the cache./// The cache item value to set in the cache./// The cache options for the value./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The indicating that the operation is asynchronous. public virtual async Task SetAsync(TCacheKey key,TCacheItem value,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{async Task SetRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{await Cache.SetAsync(NormalizeKey(key, cacheName),Serializer.Serialize(value),options ?? DefaultCacheOptions,CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out _)){uowCache[key].SetValue(value);}else{uowCache.Add(key, new UnitOfWorkCacheItem(value));}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(SetRealCache);}else{await SetRealCache();}}public void SetMany(IEnumerable> items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{var itemsArray = items.ToArray();var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;if (cacheSupportsMultipleItems == null){SetManyFallback(itemsArray,cacheName,options,hideErrors,considerUow);return;}void SetRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{cacheSupportsMultipleItems.SetMany(ToRawCacheItems(itemsArray, cacheName),options ?? DefaultCacheOptions);}catch (Exception ex){if (hideErrors == true){HandleException(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();foreach (var pair in itemsArray){if (uowCache.TryGetValue(pair.Key, out _)){uowCache[pair.Key].SetValue(pair.Value);}else{uowCache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value));}}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(() =>{SetRealCache();return Task.CompletedTask;});}else{SetRealCache();}}protected virtual void SetManyFallback(KeyValuePair[] items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{foreach (var item in items){Set(item.Key,item.Value,cacheName,options,false,considerUow);}}catch (Exception ex){if (hideErrors == true){HandleException(ex);return;}throw;}}public virtual async Task SetManyAsync(IEnumerable> items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{var itemsArray = items.ToArray();var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;if (cacheSupportsMultipleItems == null){await SetManyFallbackAsync(itemsArray,cacheName,options,hideErrors,considerUow,token);return;}async Task SetRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{await cacheSupportsMultipleItems.SetManyAsync(ToRawCacheItems(itemsArray, cacheName),options ?? DefaultCacheOptions,CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();foreach (var pair in itemsArray){if (uowCache.TryGetValue(pair.Key, out _)){uowCache[pair.Key].SetValue(pair.Value);}else{uowCache.Add(pair.Key, new UnitOfWorkCacheItem(pair.Value));}}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(SetRealCache);}else{await SetRealCache();}}protected virtual async Task SetManyFallbackAsync(KeyValuePair[] items,string cacheName,DistributedCacheEntryOptions options = null,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{foreach (var item in items){await SetAsync(item.Key,item.Value,cacheName,options,false,considerUow,token: token);}}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return;}throw;}}/// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache.public virtual void Refresh(TCacheKey key,string cacheName,bool? hideErrors = null){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{Cache.Refresh(NormalizeKey(key, cacheName));}catch (Exception ex){if (hideErrors == true){HandleException(ex);return;}throw;}}/// /// Refreshes the cache value of the given key, and resets its sliding expiration timeout./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// The for the task./// The indicating that the operation is asynchronous. public virtual async Task RefreshAsync(TCacheKey key,string cacheName,bool? hideErrors = null,CancellationToken token = default){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{await Cache.RefreshAsync(NormalizeKey(key, cacheName), CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return;}throw;}}/// /// Removes the cache item for given key from cache./// /// The key of cached item to be retrieved from the cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// Indicates to throw or hide the exceptions for the distributed cache.public virtual void Remove(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false) where TCacheItem : class{void RemoveRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{Cache.Remove(NormalizeKey(key, cacheName));}catch (Exception ex){if (hideErrors == true){HandleException(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out _)){uowCache[key].RemoveValue();}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(() =>{RemoveRealCache();return Task.CompletedTask;});}else{RemoveRealCache();}}/// /// Removes the cache item for given key from cache./// /// The key of cached item to be retrieved from the cache./// Indicates to throw or hide the exceptions for the distributed cache./// This will store the cache in the current unit of work until the end of the current unit of work does not really affect the cache./// The for the task./// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync(TCacheKey key,string cacheName,bool? hideErrors = null,bool considerUow = false,CancellationToken token = default) where TCacheItem : class{async Task RemoveRealCache(){hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;try{await Cache.RemoveAsync(NormalizeKey(key, cacheName), CancellationTokenProvider.FallbackToProvider(token));}catch (Exception ex){if (hideErrors == true){await HandleExceptionAsync(ex);return;}throw;}}if (ShouldConsiderUow(considerUow)){var uowCache = GetUnitOfWorkCache();if (uowCache.TryGetValue(key, out _)){uowCache[key].RemoveValue();}// ReSharper disable once PossibleNullReferenceExceptionUnitOfWorkManager.Current.OnCompleted(RemoveRealCache);}else{await RemoveRealCache();}}protected virtual void HandleException(Exception ex){AsyncHelper.RunSync(() => HandleExceptionAsync(ex));}protected virtual async Task HandleExceptionAsync(Exception ex){Logger.LogException(ex, LogLevel.Warning);using (var scope = ServiceScopeFactory.CreateScope()){await scope.ServiceProvider.GetRequiredService().NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));}}protected virtual KeyValuePair[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys) where TCacheItem: class{if (itemBytes.Length != itemKeys.Length){throw new AbpException("count of the item bytes should be same with the count of the given keys");}var result = new List>();for (int i = 0; i < itemKeys.Length; i++){result.Add(new KeyValuePair(itemKeys[i],ToCacheItem(itemBytes[i])));}return result.ToArray();}[CanBeNull]protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes) where TCacheItem : class{if (bytes == null){return null;}return Serializer.Deserialize(bytes);}protected virtual KeyValuePair[] ToRawCacheItems(KeyValuePair[] items, string cacheName){return items.Select(i => new KeyValuePair(NormalizeKey(i.Key, cacheName),Serializer.Serialize(i.Value))).ToArray();}private static KeyValuePair[] ToCacheItemsWithDefaultValues(TCacheKey[] keys){return keys.Select(key => new KeyValuePair(key, default)).ToArray();}protected virtual bool ShouldConsiderUow(bool considerUow){return considerUow && UnitOfWorkManager.Current != null;}protected virtual string GetUnitOfWorkCacheKey(){return UowCacheName + CacheName;}protected virtual Dictionary> GetUnitOfWorkCache() where TCacheItem : class{if (UnitOfWorkManager.Current == null){throw new AbpException($"There is no active UOW.");}return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(),key => new Dictionary>());}}[Dependency(ReplaceServices = true)]public class WantDistributedCacheKeyNormalizer : IDistributedCacheKeyNormalizer, ITransientDependency{protected AbpDistributedCacheOptions DistributedCacheOptions { get; }public SuncereDistributedCacheKeyNormalizer(IOptions distributedCacheOptions){DistributedCacheOptions = distributedCacheOptions.Value;}public virtual string NormalizeKey(DistributedCacheKeyNormalizeArgs args){// 缓存格式: a:appname,c:cachename,k:keyvar normalizedKey = $"a:{DistributedCacheOptions.KeyPrefix},c:{args.CacheName},k:{args.Key}";return normalizedKey;}}[DependsOn(typeof(AbpCachingModule))]public class WantAbpCacheModule : AbpModule{public override void ConfigureServices(ServiceConfigurationContext context){//注入缓存类context.Services.AddSingleton(typeof(ISuncereDistributedCache), typeof(SuncereDistributedCache));}}
参考文章:ABP 官方文档 - 缓存
ABP 系列总结:目录:ABP 系列总结上一篇:ABP - 缓存模块(1)
相关稿件
最近更新
• BNEF: 2050年海上风电平均价格将降至37美元/兆瓦时 环球动态
• 世界新资讯:一起同过窗网盘资源_一起同过窗2百度云资源谁有
• 耽美动漫丨集合搞笑、温馨、狗血、三角恋,以及前女友的大杂烩日漫
• 618收官:打造金融消费大生态,民生银行助经济活力释放 信息
• 【播资讯】2023广西南宁师范大学招聘高层次人才拟聘人员名单公示(6-27)
• A股异动|华建集团(600629.SH)涨超4% 下属上海院签订7408万元工程设计合同
• 环球看热讯:西山科技: 预计2023年公司收入依然会保持快速增长,具体经营情况以公司公告为准
• 阳煤化工: 公司股权转让事项正在积极推进,如有进展公司将第一时间进行披露
• “请别叫马主任,叫我店小二就行” | 高质量发展调研行・海南站
• 今日要闻!取消播放量显示、增加变现方式,B站能留住UP主吗?
• 中华文化与中国品牌美美与共: 当龙牌邂逅“龙节”,与北新一起向美而行
• 康欣新材: 截止2023年6月20日,公司股东数为39227户,|讯息