Merge pull request #7523 from abpframework/liangshiwei/cache

Add more "Many" methods to the ICacheSupportsMultipleItems
pull/7673/head
Halil İbrahim Kalkan 5 years ago committed by GitHub
commit ba83880e53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -242,12 +242,15 @@ In addition, all of the `IDistributedCache<TCacheItem>` (and `IDistributedCache<
## Batch Operations
ABP's distributed cache interfaces provide methods to perform batch get/set methods those improves the performance when you want to get or set multiple cache items in a single method call.
ABP's distributed cache interfaces provide methods to perform batch methods those improves the performance when you want to batch operation multiple cache items in a single method call.
* `SetManyAsync` and `SetMany` methods can be used to set multiple values to the cache.
* `GetManyAsync` and `GetMany` methods can be used to retrieve multiple values from the cache.
* `GetOrAddManyAsync` and `GetOrAddMany` methods can be used to retrieve multiple values and set missing values from the cache
* `RefreshManyAsync` and `RefreshMany` methods can be used to resets the sliding expiration timeout of multiple values from the cache
* `RemoveManyAsync` and `RemoveMany` methods can be used to remove multiple values from the cache
> These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` methods (called once for each item).
> These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` ... methods (called once for each item).
## Advanced Topics

@ -192,6 +192,18 @@ public class BookService : ITransientDependency
}
````
## 批量操作
ABP的分布式缓存接口定义了以下批量操作方法,当你需要在一个方法中调用多次缓存操作时,这些方法可以提高性能
* `SetManyAsync``SetMany` 方法可以用来设置多个值.
* `GetManyAsync``GetMany` 方法可以用来从缓存中获取多个值.
* `GetOrAddManyAsync``GetOrAddMany` 方法可以用来从缓存中获取并添加缺少的值.
* `RefreshManyAsync``RefreshMany` 方法可以来用重置多个值的滚动过期时间.
* `RemoveManyAsync``RemoveMany` 方法呆以用来删除多个值.
> 这些不是标准的ASP.NET Core缓存方法, 所以某些提供程序可能不支持. [ABP Redis集成包](Redis-Cache.md)实现了它们. 如果提供程序不支持,会回退到 `SetAsync``GetAsync` ... 方法(循环调用).
### DistributedCacheOptions
TODO

@ -45,19 +45,26 @@ namespace Volo.Abp.Caching.StackExchangeRedis
MapMetadataMethod = type.GetMethod("MapMetadata", BindingFlags.Instance | BindingFlags.NonPublic);
GetAbsoluteExpirationMethod = type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic);
GetAbsoluteExpirationMethod =
type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic);
GetExpirationInSecondsMethod = type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic);
GetExpirationInSecondsMethod =
type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic);
SetScript = type.GetField("SetScript", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString();
SetScript = type.GetField("SetScript", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null)
.ToString();
AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString();
AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)
?.GetValue(null).ToString();
SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString();
SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)
?.GetValue(null).ToString();
DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).ToString();
DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null).ToString();
NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null).To<int>();
// ReSharper disable once PossibleNullReferenceException
NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null)
.To<int>();
}
public AbpRedisCache(IOptions<RedisCacheOptions> optionsAccessor)
@ -124,6 +131,42 @@ namespace Volo.Abp.Caching.StackExchangeRedis
await Task.WhenAll(PipelineSetMany(items, options));
}
public void RefreshMany(
IEnumerable<string> keys)
{
keys = Check.NotNull(keys, nameof(keys));
GetAndRefreshMany(keys, false);
}
public async Task RefreshManyAsync(
IEnumerable<string> keys,
CancellationToken token = default)
{
keys = Check.NotNull(keys, nameof(keys));
await GetAndRefreshManyAsync(keys, false, token);
}
public void RemoveMany(IEnumerable<string> keys)
{
keys = Check.NotNull(keys, nameof(keys));
Connect();
RedisDatabase.KeyDelete(keys.Select(key => (RedisKey)(Instance + key)).ToArray());
}
public async Task RemoveManyAsync(IEnumerable<string> 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<string> keys,
bool getData)

@ -527,6 +527,198 @@ namespace Volo.Abp.Caching
return value;
}
public KeyValuePair<TCacheKey, TCacheItem>[] GetOrAddMany(
IEnumerable<TCacheKey> keys,
Func<IEnumerable<TCacheKey>, List<KeyValuePair<TCacheKey, TCacheItem>>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false)
{
KeyValuePair<TCacheKey, TCacheItem>[] result;
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
result = GetManyFallback(
keyArray,
hideErrors,
considerUow
);
}
else
{
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(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(NormalizeKey));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return ToCacheItemsWithDefaultValues(keyArray);
}
throw;
}
result = cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
if (result.All(x => x.Value != null))
{
return result;
}
var missingKeys = new List<TCacheKey>();
var missingValuesIndex = new List<int>();
for (var i = 0; i < keyArray.Length; i++)
{
if (result[i].Value != null)
{
continue;
}
missingKeys.Add(keyArray[i]);
missingValuesIndex.Add(i);
}
var missingValues = factory.Invoke(missingKeys).ToArray();
var valueQueue = new Queue<KeyValuePair<TCacheKey, TCacheItem>>(missingValues);
SetMany(missingValues, optionsFactory?.Invoke(), hideErrors, considerUow);
foreach (var index in missingValuesIndex)
{
result[index] = valueQueue.Dequeue();
}
return result;
}
public async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetOrAddManyAsync(
IEnumerable<TCacheKey> keys,
Func<IEnumerable<TCacheKey>, Task<List<KeyValuePair<TCacheKey, TCacheItem>>>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default)
{
KeyValuePair<TCacheKey, TCacheItem>[] result;
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
result = await GetManyFallbackAsync(
keyArray,
hideErrors,
considerUow, token);
}
else
{
var notCachedKeys = new List<TCacheKey>();
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = uowCache.GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
cachedValues.Add(new KeyValuePair<TCacheKey, TCacheItem>(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(NormalizeKey), token);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues(keyArray);
}
throw;
}
result = cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
}
if (result.All(x => x.Value != null))
{
return result;
}
var missingKeys = new List<TCacheKey>();
var missingValuesIndex = new List<int>();
for (var i = 0; i < keyArray.Length; i++)
{
if (result[i].Value != null)
{
continue;
}
missingKeys.Add(keyArray[i]);
missingValuesIndex.Add(i);
}
var missingValues = (await factory.Invoke(missingKeys)).ToArray();
var valueQueue = new Queue<KeyValuePair<TCacheKey, TCacheItem>>(missingValues);
await SetManyAsync(missingValues, optionsFactory?.Invoke(), hideErrors, considerUow, token);
foreach (var index in missingValuesIndex)
{
result[index] = valueQueue.Dequeue();
}
return result;
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -924,6 +1116,71 @@ namespace Volo.Abp.Caching
}
}
public virtual void RefreshMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
if (Cache is ICacheSupportsMultipleItems cacheSupportsMultipleItems)
{
cacheSupportsMultipleItems.RefreshMany(keys.Select(NormalizeKey));
}
else
{
foreach (var key in keys)
{
Cache.Refresh(NormalizeKey(key));
}
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
public virtual async Task RefreshManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
if (Cache is ICacheSupportsMultipleItems cacheSupportsMultipleItems)
{
await cacheSupportsMultipleItems.RefreshManyAsync(keys.Select(NormalizeKey), token);
}
else
{
foreach (var key in keys)
{
await Cache.RefreshAsync(NormalizeKey(key), token);
}
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
@ -1027,6 +1284,130 @@ namespace Volo.Abp.Caching
}
}
public void RemoveMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
bool considerUow = false)
{
var keyArray = keys.ToArray();
if (Cache is ICacheSupportsMultipleItems cacheSupportsMultipleItems)
{
void RemoveRealCache()
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
cacheSupportsMultipleItems.RemoveMany(
keyArray.Select(NormalizeKey)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(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();
return Task.CompletedTask;
});
}
else
{
RemoveRealCache();
}
}
else
{
foreach (var key in keyArray)
{
Remove(key, hideErrors, considerUow);
}
}
}
public async Task RemoveManyAsync(
IEnumerable<TCacheKey> 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)
{
_ = HandleExceptionAsync(ex);

@ -25,6 +25,24 @@ namespace Volo.Abp.Caching
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options,
CancellationToken token = default
);
);
void RefreshMany(
IEnumerable<string> keys
);
Task RefreshManyAsync(
IEnumerable<string> keys,
CancellationToken token = default
);
void RemoveMany(
IEnumerable<string> keys
);
Task RemoveManyAsync(
IEnumerable<string> keys,
CancellationToken token = default
);
}
}
}

@ -14,8 +14,8 @@ namespace Volo.Abp.Caching
public interface IDistributedCache<TCacheItem> : IDistributedCache<TCacheItem, string>
where TCacheItem : class
{
}
/// <summary>
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// Uses a generic cache key type of <typeparamref name="TCacheKey" /> type.
@ -128,6 +128,44 @@ namespace Volo.Abp.Caching
CancellationToken token = default
);
/// <summary>
/// Gets or Adds multiple cache items with the given keys. If any cache items not found for the given keys then adds cache items
/// provided by <paramref name="factory" /> delegate and returns the provided cache items.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache items when no cache items are found for the given <paramref name="keys" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">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.</param>
/// <returns>The cache items.</returns>
KeyValuePair<TCacheKey, TCacheItem>[] GetOrAddMany(
IEnumerable<TCacheKey> keys,
Func<IEnumerable<TCacheKey>, List<KeyValuePair<TCacheKey, TCacheItem>>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
/// Gets or Adds multiple cache items with the given keys. If any cache items not found for the given keys then adds cache items
/// provided by <paramref name="factory" /> delegate and returns the provided cache items.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache items when no cache items are found for the given <paramref name="keys" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">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.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache items.</returns>
Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetOrAddManyAsync(
IEnumerable<TCacheKey> keys,
Func<IEnumerable<TCacheKey>, Task<List<KeyValuePair<TCacheKey, TCacheItem>>>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -219,6 +257,29 @@ namespace Volo.Abp.Caching
CancellationToken token = default
);
/// <summary>
/// Refreshes the cache value of the given keys, and resets their sliding expiration timeout.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void RefreshMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null);
/// <summary>
/// Refreshes the cache value of the given keys, and resets their sliding expiration timeout.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RefreshManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
CancellationToken token = default);
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
@ -245,5 +306,32 @@ namespace Volo.Abp.Caching
bool considerUow = false,
CancellationToken token = default
);
/// <summary>
/// Removes the cache items for given keys from cache.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">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.</param>
void RemoveMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
bool considerUow = false
);
/// <summary>
/// Removes the cache items for given keys from cache.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <param name="considerUow">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.</param>
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RemoveManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
bool considerUow = false,
CancellationToken token = default
);
}
}

@ -712,5 +712,240 @@ namespace Volo.Abp.Caching
(await personCache.GetAsync("john")).Name.ShouldBe("John Nash");
(await personCache.GetAsync("baris")).ShouldBeNull();
}
[Fact]
public async Task Should_Get_And_Add_Multiple_Items_Async()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(testkey, new PersonCacheItem("john"));
var cacheValue = await personCache.GetManyAsync(testkey3);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.ShouldBeNull();
cacheValue = await personCache.GetOrAddManyAsync(testkey3, (missingKeys) =>
{
var missingKeyArray = missingKeys.ToArray();
missingKeyArray.Length.ShouldBe(1);
missingKeyArray[0].ShouldBe(testkey2);
return Task.FromResult(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey2, new PersonCacheItem("jack"))
});
});
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
cacheValue = await personCache.GetManyAsync(testkey3);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetOrAddManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetOrAddManyAsync(testkey3, (missingKeys) => Task.FromResult(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey, new PersonCacheItem("john")),
new(testkey2, new PersonCacheItem("jack")),
}), considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetOrAddManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetOrAddManyAsync(testkey3, (missingKeys) => Task.FromResult(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey, new PersonCacheItem("john")),
new(testkey2, new PersonCacheItem("jack")),
}), considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
}
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
}
[Fact]
public async Task Should_Remove_Multiple_Items_Async()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey, new PersonCacheItem("john")),
new(testkey2, new PersonCacheItem("jack"))
});
await personCache.RemoveManyAsync(testkey3);
var cacheValue = await personCache.GetManyAsync(testkey3);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_RemoveManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey, new PersonCacheItem("john")),
new(testkey2, new PersonCacheItem("jack"))
});
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
await personCache.RemoveManyAsync(testkey3, considerUow: true);
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_RemoveManyAsync()
{
var testkey = "testkey";
var testkey2 = "testkey2";
var testkey3 = new[] {testkey, testkey2};
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetManyAsync(testkey3, considerUow: false);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetManyAsync(new List<KeyValuePair<string, PersonCacheItem>>
{
new(testkey, new PersonCacheItem("john")),
new(testkey2, new PersonCacheItem("jack"))
}, considerUow: true);
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.Name.ShouldBe("john");
cacheValue[1].Value.Name.ShouldBe("jack");
await personCache.RemoveManyAsync(testkey3, considerUow: true);
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
}
cacheValue = await personCache.GetManyAsync(testkey3, considerUow: true);
cacheValue.Length.ShouldBe(2);
cacheValue[0].Value.ShouldBeNull();
cacheValue[1].Value.ShouldBeNull();
}
}
}

@ -57,5 +57,37 @@ namespace Volo.Abp.Caching
await SetAsync(item.Key, item.Value, options, token);
}
}
public void RefreshMany(IEnumerable<string> keys)
{
foreach (var key in keys)
{
Refresh(key);
}
}
public async Task RefreshManyAsync(IEnumerable<string> keys, CancellationToken token = default)
{
foreach (var key in keys)
{
await RefreshAsync(key, token);
}
}
public void RemoveMany(IEnumerable<string> keys)
{
foreach (var key in keys)
{
Remove(key);
}
}
public async Task RemoveManyAsync(IEnumerable<string> keys, CancellationToken token = default)
{
foreach (var key in keys)
{
await RemoveAsync(key, token);
}
}
}
}

Loading…
Cancel
Save