diff --git a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs index dbf04a1d80..fc45ccd05d 100644 --- a/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs +++ b/framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs @@ -148,6 +148,25 @@ namespace Volo.Abp.Caching.StackExchangeRedis await GetAndRefreshManyAsync(keys, false, token); } + public void RemoveMany(IEnumerable keys) + { + keys = Check.NotNull(keys, nameof(keys)); + + Connect(); + + RedisDatabase.KeyDelete(keys.Select(key => (RedisKey)(Instance + key)).ToArray()); + } + + public async Task RemoveManyAsync(IEnumerable keys, CancellationToken token = default) + { + keys = Check.NotNull(keys, nameof(keys)); + + token.ThrowIfCancellationRequested(); + await ConnectAsync(token); + + await RedisDatabase.KeyDeleteAsync(keys.Select(key => (RedisKey)(Instance + key)).ToArray()); + } + protected virtual byte[][] GetAndRefreshMany( IEnumerable keys, bool getData) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 2ebe9b2fb4..5c9a876f4b 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -1284,28 +1284,128 @@ namespace Volo.Abp.Caching } } - protected virtual KeyValuePair[] GetOrAddMany( - TCacheKey[] keys, - KeyValuePair[] values, - KeyValuePair[] missingValues) + public void RemoveMany( + IEnumerable keys, + bool? hideErrors = null, + bool considerUow = false) { - var missingValuesIndex = new List(); - for (var i = 0; i < keys.Length; i++) + var keyArray = keys.ToArray(); + + if (Cache is ICacheSupportsMultipleItems cacheSupportsMultipleItems) { - if (values[i].Value == null) + void RemoveRealCache() { - missingValuesIndex.Add(i); + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + cacheSupportsMultipleItems.RemoveMany( + keyArray.Select(NormalizeKey) + ); + } + catch (Exception ex) + { + if (hideErrors == true) + { + HandleException(ex); + return; + } + + throw; + } } - } - var valueQueue = new Queue>(missingValues); + if (ShouldConsiderUow(considerUow)) + { + var uowCache = GetUnitOfWorkCache(); - foreach (var index in missingValuesIndex) + foreach (var key in keyArray) + { + if (uowCache.TryGetValue(key, out _)) + { + uowCache[key].RemoveValue(); + } + } + + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(() => + { + RemoveRealCache(); + return Task.CompletedTask; + }); + } + else + { + RemoveRealCache(); + } + } + else { - values[index] = valueQueue.Dequeue(); + foreach (var key in keyArray) + { + Remove(key, hideErrors, considerUow); + } } + } - return values; + public async Task RemoveManyAsync( + IEnumerable keys, + bool? hideErrors = null, + bool considerUow = false, + CancellationToken token = default) + { + var keyArray = keys.ToArray(); + + if (Cache is ICacheSupportsMultipleItems cacheSupportsMultipleItems) + { + async Task RemoveRealCache() + { + hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; + + try + { + await cacheSupportsMultipleItems.RemoveManyAsync( + keyArray.Select(NormalizeKey), token); + } + catch (Exception ex) + { + if (hideErrors == true) + { + await HandleExceptionAsync(ex); + return; + } + + throw; + } + } + + if (ShouldConsiderUow(considerUow)) + { + var uowCache = GetUnitOfWorkCache(); + + foreach (var key in keyArray) + { + if (uowCache.TryGetValue(key, out _)) + { + uowCache[key].RemoveValue(); + } + } + + // ReSharper disable once PossibleNullReferenceException + UnitOfWorkManager.Current.OnCompleted(RemoveRealCache); + } + else + { + await RemoveRealCache(); + } + } + else + { + foreach (var key in keyArray) + { + await RemoveAsync(key, hideErrors, considerUow, token); + } + } } protected virtual void HandleException(Exception ex) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs index ec73a159a4..857385699d 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; @@ -36,5 +35,14 @@ namespace Volo.Abp.Caching IEnumerable keys, CancellationToken token = default ); + + void RemoveMany( + IEnumerable keys + ); + + Task RemoveManyAsync( + IEnumerable keys, + CancellationToken token = default + ); } } diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index d8510efe43..1d64520898 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -306,5 +306,32 @@ namespace Volo.Abp.Caching bool considerUow = false, CancellationToken token = default ); + + /// + /// Removes the cache items for given keys from 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. + void RemoveMany( + IEnumerable keys, + bool? hideErrors = null, + bool considerUow = false + ); + + /// + /// Removes the cache items for given keys from 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. + /// The indicating that the operation is asynchronous. + Task RemoveManyAsync( + IEnumerable keys, + bool? hideErrors = null, + bool considerUow = false, + CancellationToken token = default + ); } } diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs index 4490a50fd3..5df5b488d9 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs +++ b/framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs @@ -73,5 +73,21 @@ namespace Volo.Abp.Caching await RefreshAsync(key, token); } } + + public void RemoveMany(IEnumerable keys) + { + foreach (var key in keys) + { + Remove(key); + } + } + + public async Task RemoveManyAsync(IEnumerable keys, CancellationToken token = default) + { + foreach (var key in keys) + { + await RemoveAsync(key, token); + } + } } }