订阅

首页

天天日报丨ABP - 缓存模块(2)

博客园 2023-06-27 13:20:14


(资料图片)

1. 缓存模块源码解析

个人觉得 ABP 分布式缓存模块有三个值得关注的核心点。首先是 AbpRedisCache 类继承了微软原生的 RedisCache,并 通过反射的方式获取RedisCache的私有方法对 RedisCache 进行扩展,实现了 ABP 分布式缓存中的批量操作方法。

为什么要这么做呢?因为基于 Redis 缓存的批量操作需要使用到 StackExchange.Redis 原生的SDK,而且在进行操作前总需要先进行 Redis 连接,而相应的方法和属性原生的 RedisCache 中都有,但是都是私有(private)的,在继承类中也无法使用,所以使用反射的方式提取出相应的方法和属性,以便在继承类中复用。这也是对于类功能进行继承扩展的时候的一种很有用的方式。

第二点是 ABP 缓存模块通过 IDistributedCache 接口扩展了原生的 IDistributedCache 接口的功能,而在具体实现上是将原有的 IDistributedCache 服务注入进行复用相应功能的,并在前后增加额外的逻辑对功能进行扩展增强,实际上就是适配器模式。

而最常使用的 IDistributedCache接口,以前其实现类是直接继承 DistributedCache 的,现在改成了直接用适配器模式。

最后一个是缓存的事务性,通过工作单元使缓存和其他事务操作保持原子性,避免缓存已经更新而其他事务失败回滚导致数据不一致的问题。实际上就是先不真正地更新缓存,而是将缓存数据通过字典保存在工作单元中,保证一个工作单元内拿到的缓存数据是最新的,同时注册工作单元提交事件,在工作单元正在提交成功的时候才执行真正更新缓存的逻辑。

工作单元相关的内容就后面再在专门的章节讲吧,这部分的内容比较复杂,一时半会比较难讲清。

2. 自己扩展的 IDistributedCache

ABP 框架扩展的 IDistributedCache泛型接口在内部帮我们处理实例对象进行序列化/反序列化以及转码为 byte 数组的问题,大大提升了我们使用分布式缓存的方便性,但也存在一些问题。

它是基于类的泛型,如果我们在一个服务中需要使用多个缓存类型的话,我们就得注入多个的泛型接口,还是有些不方便的它的缓存键是以默认是以泛型类型的全类名作为前缀,虽然我们可以通过特性指定类型名称,但是对于集合,缓存键就很不清晰了由于这些情况的存在,也基于我们在日常开发中的使用习惯,我在工作中又基于 ABP 的 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)

相关稿件

最近更新

天天日报丨ABP - 缓存模块(2)

拜登宣布420亿美元高速互联网计划

美国国会:除付费版ChatGPT外,不得使用其他产品

每日聚焦:罗永浩6亿债务已还完?公司回应

BNEF: 2050年海上风电平均价格将降至37美元/兆瓦时 环球动态

快资讯:亿赛通加密简单破解方法 亿赛通加密简单破解

世界新资讯:一起同过窗网盘资源_一起同过窗2百度云资源谁有

耽美动漫丨集合搞笑、温馨、狗血、三角恋,以及前女友的大杂烩日漫

湖南省全面排查整治城镇燃气8类安全隐患 环球滚动

制作粗糙,问题成堆,泰坦号在海中内爆成碎片

世界滚动:小米汽车14.99万起售?小米王化:不实消息

环球快消息!单向好友删除器网址_单向好友删除

618收官:打造金融消费大生态,民生银行助经济活力释放 信息

【播资讯】2023广西南宁师范大学招聘高层次人才拟聘人员名单公示(6-27)

电脑上本地磁盘c满了怎么办_本地磁盘c满了怎么办

A股异动|华建集团(600629.SH)涨超4% 下属上海院签订7408万元工程设计合同

环球看热讯:西山科技: 预计2023年公司收入依然会保持快速增长,具体经营情况以公司公告为准

阳煤化工: 公司股权转让事项正在积极推进,如有进展公司将第一时间进行披露

“请别叫马主任,叫我店小二就行” | 高质量发展调研行・海南站

天天精选!特斯拉股价跌超6%,评级遭华尔街大行密集下调

今日要闻!取消播放量显示、增加变现方式,B站能留住UP主吗?

速卖通加大巴西海外仓投入 最快可实现5日达

宾利首款纯电动车将在2025年亮相

中华文化与中国品牌美美与共: 当龙牌邂逅“龙节”,与北新一起向美而行

联想集团发布ESG报告 承诺2050年实现净零排放

广州市国企资产总额突破6万亿元|世界时快讯

康欣新材: 截止2023年6月20日,公司股东数为39227户,|讯息

威高骨科: 带量采购目前已经全面执行,公司手术量增长明显,具体数据请持续关注公司公告-快看

亩产1200多公斤!惠城汝湖村尾村撂荒地种甜玉米获丰收

最资讯丨2023好网民·在山东|张立敏:讲好医患暖心故事