Browse Source

Storage cache in the unit of work.

Resolve #4040
pull/4523/head
maliming 6 years ago
parent
commit
f8a251afb9
  1. 2
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs
  2. 522
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  3. 24
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
  4. 43
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs
  5. 11
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs
  6. 2
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  7. 43
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs
  8. 230
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs
  9. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs
  10. 51
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs
  11. 11
      modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs
  12. 34
      modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs

2
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/AbpCachingModule.cs

@ -5,12 +5,14 @@ using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Serialization; using Volo.Abp.Serialization;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching
{ {
[DependsOn( [DependsOn(
typeof(AbpThreadingModule), typeof(AbpThreadingModule),
typeof(AbpSerializationModule), typeof(AbpSerializationModule),
typeof(AbpUnitOfWorkModule),
typeof(AbpMultiTenancyModule), typeof(AbpMultiTenancyModule),
typeof(AbpJsonModule))] typeof(AbpJsonModule))]
public class AbpCachingModule : AbpModule public class AbpCachingModule : AbpModule

522
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs

@ -14,6 +14,7 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.ExceptionHandling; using Volo.Abp.ExceptionHandling;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching
{ {
@ -30,13 +31,15 @@ namespace Volo.Abp.Caching
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer, IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer, IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory) : base( IHybridServiceScopeFactory serviceScopeFactory,
distributedCacheOption: distributedCacheOption, IUnitOfWorkManager unitOfWorkManager) : base(
cache: cache, distributedCacheOption: distributedCacheOption,
cancellationTokenProvider: cancellationTokenProvider, cache: cache,
serializer: serializer, cancellationTokenProvider: cancellationTokenProvider,
keyNormalizer: keyNormalizer, serializer: serializer,
serviceScopeFactory: serviceScopeFactory) keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory,
unitOfWorkManager:unitOfWorkManager)
{ {
} }
} }
@ -50,6 +53,8 @@ namespace Volo.Abp.Caching
public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheItem, TCacheKey> public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheItem, TCacheKey>
where TCacheItem : class where TCacheItem : class
{ {
public const string DistributedCacheName = "AbpDistributedCache";
public ILogger<DistributedCache<TCacheItem, TCacheKey>> Logger { get; set; } public ILogger<DistributedCache<TCacheItem, TCacheKey>> Logger { get; set; }
protected string CacheName { get; set; } protected string CacheName { get; set; }
@ -66,6 +71,8 @@ namespace Volo.Abp.Caching
protected IHybridServiceScopeFactory ServiceScopeFactory { get; } protected IHybridServiceScopeFactory ServiceScopeFactory { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected SemaphoreSlim SyncSemaphore { get; } protected SemaphoreSlim SyncSemaphore { get; }
protected DistributedCacheEntryOptions DefaultCacheOptions; protected DistributedCacheEntryOptions DefaultCacheOptions;
@ -78,7 +85,8 @@ namespace Volo.Abp.Caching
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer, IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer, IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory) IHybridServiceScopeFactory serviceScopeFactory,
IUnitOfWorkManager unitOfWorkManager)
{ {
_distributedCacheOption = distributedCacheOption.Value; _distributedCacheOption = distributedCacheOption.Value;
Cache = cache; Cache = cache;
@ -87,6 +95,7 @@ namespace Volo.Abp.Caching
Serializer = serializer; Serializer = serializer;
KeyNormalizer = keyNormalizer; KeyNormalizer = keyNormalizer;
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
UnitOfWorkManager = unitOfWorkManager;
SyncSemaphore = new SemaphoreSlim(1, 1); SyncSemaphore = new SemaphoreSlim(1, 1);
@ -133,14 +142,25 @@ namespace Volo.Abp.Caching
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item, or null.</returns> /// <returns>The cache item, or null.</returns>
public virtual TCacheItem Get( public virtual TCacheItem Get(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes; byte[] cachedBytes;
try try
@ -163,6 +183,7 @@ namespace Volo.Abp.Caching
public virtual KeyValuePair<TCacheKey, TCacheItem>[] GetMany( public virtual KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys, IEnumerable<TCacheKey> keys,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
var keyArray = keys.ToArray(); var keyArray = keys.ToArray();
@ -172,33 +193,57 @@ namespace Volo.Abp.Caching
{ {
return GetManyFallback( return GetManyFallback(
keyArray, keyArray,
considerUow,
hideErrors hideErrors
); );
} }
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
var notCachedKeys = new List<TCacheKey>();
if (ShouldConsiderUow(considerUow))
{
var cache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = cache.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; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes; byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try try
{ {
cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey)); cachedBytes = cacheSupportsMultipleItems.GetMany(readKeys.Select(NormalizeKey));
} }
catch (Exception ex) catch (Exception ex)
{ {
if (hideErrors == true) if (hideErrors == true)
{ {
HandleException(ex); HandleException(ex);
return ToCacheItemsWithDefaultValues(keyArray); return ToCacheItemsWithDefaultValues(readKeys);
} }
throw; throw;
} }
return ToCacheItems(cachedBytes, keyArray); return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
} }
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] GetManyFallback( protected virtual KeyValuePair<TCacheKey, TCacheItem>[] GetManyFallback(
TCacheKey[] keys, TCacheKey[] keys,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -208,7 +253,7 @@ namespace Volo.Abp.Caching
return keys return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>( .Select(key => new KeyValuePair<TCacheKey, TCacheItem>(
key, key,
Get(key, hideErrors: false) Get(key, considerUow, hideErrors: false)
) )
).ToArray(); ).ToArray();
} }
@ -226,6 +271,7 @@ namespace Volo.Abp.Caching
public virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync( public virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys, IEnumerable<TCacheKey> keys,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -236,18 +282,42 @@ namespace Volo.Abp.Caching
{ {
return await GetManyFallbackAsync( return await GetManyFallbackAsync(
keyArray, keyArray,
considerUow,
hideErrors, hideErrors,
token token
); );
} }
var cachedValues = new List<KeyValuePair<TCacheKey, TCacheItem>>();
var notCachedKeys = new List<TCacheKey>();
if (ShouldConsiderUow(considerUow))
{
var cache = GetUnitOfWorkCache();
foreach (var key in keyArray)
{
var value = cache.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; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes; byte[][] cachedBytes;
var readKeys = notCachedKeys.Any() ? notCachedKeys.ToArray() : keyArray;
try try
{ {
cachedBytes = await cacheSupportsMultipleItems.GetManyAsync( cachedBytes = await cacheSupportsMultipleItems.GetManyAsync(
keyArray.Select(NormalizeKey), readKeys.Select(NormalizeKey),
CancellationTokenProvider.FallbackToProvider(token) CancellationTokenProvider.FallbackToProvider(token)
); );
} }
@ -256,17 +326,18 @@ namespace Volo.Abp.Caching
if (hideErrors == true) if (hideErrors == true)
{ {
await HandleExceptionAsync(ex); await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues(keyArray); return ToCacheItemsWithDefaultValues(readKeys);
} }
throw; throw;
} }
return ToCacheItems(cachedBytes, keyArray); return cachedValues.Concat(ToCacheItems(cachedBytes, readKeys)).ToArray();
} }
protected virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyFallbackAsync( protected virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyFallbackAsync(
TCacheKey[] keys, TCacheKey[] keys,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -280,7 +351,7 @@ namespace Volo.Abp.Caching
{ {
result.Add(new KeyValuePair<TCacheKey, TCacheItem>( result.Add(new KeyValuePair<TCacheKey, TCacheItem>(
key, key,
await GetAsync(key, false, token)) await GetAsync(key, considerUow, hideErrors: false, token: token))
); );
} }
@ -302,16 +373,27 @@ namespace Volo.Abp.Caching
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns> /// <returns>The cache item, or null.</returns>
public virtual async Task<TCacheItem> GetAsync( public virtual async Task<TCacheItem> GetAsync(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
if (ShouldConsiderUow(considerUow))
{
var value = GetUnitOfWorkCache().GetOrDefault(key)?.GetUnRemovedValueOrNull();
if (value != null)
{
return value;
}
}
byte[] cachedBytes; byte[] cachedBytes;
try try
@ -347,15 +429,17 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param> /// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param> /// <param name="optionsFactory">The cache options for the factory delegate.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item.</returns> /// <returns>The cache item.</returns>
public virtual TCacheItem GetOrAdd( public virtual TCacheItem GetOrAdd(
TCacheKey key, TCacheKey key,
Func<TCacheItem> factory, Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
var value = Get(key, hideErrors); var value = Get(key, considerUow, hideErrors);
if (value != null) if (value != null)
{ {
return value; return value;
@ -363,14 +447,28 @@ namespace Volo.Abp.Caching
using (SyncSemaphore.Lock()) using (SyncSemaphore.Lock())
{ {
value = Get(key, hideErrors); value = Get(key, considerUow, hideErrors);
if (value != null) if (value != null)
{ {
return value; return value;
} }
value = factory(); value = factory();
Set(key, value, optionsFactory?.Invoke(), hideErrors);
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
Set(key, value, optionsFactory?.Invoke(), considerUow, hideErrors);
} }
return value; return value;
@ -384,17 +482,19 @@ namespace Volo.Abp.Caching
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param> /// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</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="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> /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns> /// <returns>The cache item.</returns>
public virtual async Task<TCacheItem> GetOrAddAsync( public virtual async Task<TCacheItem> GetOrAddAsync(
TCacheKey key, TCacheKey key,
Func<Task<TCacheItem>> factory, Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
token = CancellationTokenProvider.FallbackToProvider(token); token = CancellationTokenProvider.FallbackToProvider(token);
var value = await GetAsync(key, hideErrors, token); var value = await GetAsync(key, considerUow, hideErrors, token);
if (value != null) if (value != null)
{ {
return value; return value;
@ -402,14 +502,28 @@ namespace Volo.Abp.Caching
using (await SyncSemaphore.LockAsync(token)) using (await SyncSemaphore.LockAsync(token))
{ {
value = await GetAsync(key, hideErrors, token); value = await GetAsync(key, considerUow, hideErrors, token);
if (value != null) if (value != null)
{ {
return value; return value;
} }
value = await factory(); value = await factory();
await SetAsync(key, value, optionsFactory?.Invoke(), hideErrors, token);
if (ShouldConsiderUow(considerUow))
{
var uowCache = GetUnitOfWorkCache();
if (uowCache.TryGetValue(key, out var item))
{
item.SetValue(value);
}
else
{
uowCache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
}
await SetAsync(key, value, optionsFactory?.Invoke(), considerUow, hideErrors, token);
} }
return value; return value;
@ -421,41 +535,70 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param> /// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Set( public virtual void Set(
TCacheKey key, TCacheKey key,
TCacheItem value, TCacheItem value,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; void SetRealCache()
try
{ {
Cache.Set( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
NormalizeKey(key),
Serializer.Serialize(value), try
options ?? DefaultCacheOptions {
); Cache.Set(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
} }
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{ {
if (hideErrors == true) var cache = GetUnitOfWorkCache();
if (cache.TryGetValue(key, out _))
{ {
HandleException(ex); cache[key].SetValue(value);
return; }
else
{
cache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
} }
throw; // ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
} }
} }
/// <summary> /// <summary>
/// Sets the cache item value for the provided key. /// Sets the cache item value for the provided key.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param> /// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <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> /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
@ -463,35 +606,60 @@ namespace Volo.Abp.Caching
TCacheKey key, TCacheKey key,
TCacheItem value, TCacheItem value,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; async Task SetRealCache()
try
{
await Cache.SetAsync(
NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{ {
if (hideErrors == true) hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{ {
await HandleExceptionAsync(ex); await Cache.SetAsync(
return; NormalizeKey(key),
Serializer.Serialize(value),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
} }
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw; throw;
}
} }
if (ShouldConsiderUow(considerUow))
{
var cache = GetUnitOfWorkCache();
if (cache.TryGetValue(key, out _))
{
cache[key].SetValue(value);
}
else
{
cache.Add(key, new UnitOfWorkCacheItem<TCacheItem>(value));
}
// ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
}
} }
public void SetMany( public void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items, IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
var itemsArray = items.ToArray(); var itemsArray = items.ToArray();
@ -502,36 +670,69 @@ namespace Volo.Abp.Caching
SetManyFallback( SetManyFallback(
itemsArray, itemsArray,
options, options,
considerUow,
hideErrors hideErrors
); );
return; return;
} }
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; void SetRealCache()
try
{ {
cacheSupportsMultipleItems.SetMany( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions try
); {
cacheSupportsMultipleItems.SetMany(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
} }
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{ {
if (hideErrors == true) var cache = GetUnitOfWorkCache();
foreach (var pair in itemsArray)
{ {
HandleException(ex); if (cache.TryGetValue(pair.Key, out _))
return; {
cache[pair.Key].SetValue(pair.Value);
}
else
{
cache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
} }
throw; // ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
SetRealCache();
return Task.CompletedTask;
});
}
else
{
SetRealCache();
} }
} }
protected virtual void SetManyFallback( protected virtual void SetManyFallback(
KeyValuePair<TCacheKey, TCacheItem>[] items, KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -543,7 +744,8 @@ namespace Volo.Abp.Caching
Set( Set(
item.Key, item.Key,
item.Value, item.Value,
options: options, options,
considerUow,
hideErrors: false hideErrors: false
); );
} }
@ -563,6 +765,7 @@ namespace Volo.Abp.Caching
public virtual async Task SetManyAsync( public virtual async Task SetManyAsync(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items, IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -574,6 +777,7 @@ namespace Volo.Abp.Caching
await SetManyFallbackAsync( await SetManyFallbackAsync(
itemsArray, itemsArray,
options, options,
considerUow,
hideErrors, hideErrors,
token token
); );
@ -581,31 +785,59 @@ namespace Volo.Abp.Caching
return; return;
} }
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; async Task SetRealCache()
try
{ {
await cacheSupportsMultipleItems.SetManyAsync( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions, try
CancellationTokenProvider.FallbackToProvider(token) {
); await cacheSupportsMultipleItems.SetManyAsync(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
} }
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{ {
if (hideErrors == true) var cache = GetUnitOfWorkCache();
foreach (var pair in itemsArray)
{ {
await HandleExceptionAsync(ex); if (cache.TryGetValue(pair.Key, out _))
return; {
cache[pair.Key].SetValue(pair.Value);
}
else
{
cache.Add(pair.Key, new UnitOfWorkCacheItem<TCacheItem>(pair.Value));
}
} }
throw; // ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(SetRealCache);
}
else
{
await SetRealCache();
} }
} }
protected virtual async Task SetManyFallbackAsync( protected virtual async Task SetManyFallbackAsync(
KeyValuePair<TCacheKey, TCacheItem>[] items, KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
@ -618,7 +850,8 @@ namespace Volo.Abp.Caching
await SetAsync( await SetAsync(
item.Key, item.Key,
item.Value, item.Value,
options: options, options,
considerUow,
hideErrors: false, hideErrors: false,
token: token token: token
); );
@ -636,9 +869,14 @@ namespace Volo.Abp.Caching
} }
} }
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Refresh( public virtual void Refresh(
TCacheKey key, bool? TCacheKey key,
hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -658,6 +896,13 @@ namespace Volo.Abp.Caching
} }
} }
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
/// <param name="key">The key of cached item 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>
public virtual async Task RefreshAsync( public virtual async Task RefreshAsync(
TCacheKey key, TCacheKey key,
bool? hideErrors = null, bool? hideErrors = null,
@ -681,48 +926,106 @@ namespace Volo.Abp.Caching
} }
} }
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Remove( public virtual void Remove(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null) bool? hideErrors = null)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; void RemoveRealCache()
try
{ {
Cache.Remove(NormalizeKey(key)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
Cache.Remove(NormalizeKey(key));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
} }
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{ {
if (hideErrors == true) var cache = GetUnitOfWorkCache();
if (cache.TryGetValue(key, out _))
{ {
HandleException(ex); cache[key].RemoveValue();
return;
} }
throw; // ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(() =>
{
RemoveRealCache();
return Task.CompletedTask;
});
}
else
{
RemoveRealCache();
} }
} }
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
/// <param name="key">The key of cached item to be retrieved from the 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="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>
public virtual async Task RemoveAsync( public virtual async Task RemoveAsync(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default) CancellationToken token = default)
{ {
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors; async Task RemoveRealCache()
try
{ {
await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token)); hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await Cache.RemoveAsync(NormalizeKey(key), CancellationTokenProvider.FallbackToProvider(token));
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
} }
catch (Exception ex)
if (ShouldConsiderUow(considerUow))
{ {
if (hideErrors == true) var cache = GetUnitOfWorkCache();
if (cache.TryGetValue(key, out _))
{ {
await HandleExceptionAsync(ex); cache[key].RemoveValue();
return;
} }
throw; // ReSharper disable once PossibleNullReferenceException
UnitOfWorkManager.Current.OnCompleted(RemoveRealCache);
}
else
{
await RemoveRealCache();
} }
} }
@ -793,5 +1096,26 @@ namespace Volo.Abp.Caching
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(key, default)) .Select(key => new KeyValuePair<TCacheKey, TCacheItem>(key, default))
.ToArray(); .ToArray();
} }
protected virtual bool ShouldConsiderUow(bool considerUow)
{
return considerUow && UnitOfWorkManager.Current != null;
}
protected virtual string GetUnitOfWorkCacheKey()
{
return DistributedCacheName + CacheName;
}
protected virtual Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>> GetUnitOfWorkCache()
{
if (UnitOfWorkManager.Current == null)
{
throw new AbpException($"There is no unit of work in the current context, The {GetType().Name} can only be used in a unit of work.");
}
return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(),
key => new Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>>());
}
} }
} }

24
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs

@ -29,10 +29,12 @@ namespace Volo.Abp.Caching
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null. /// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item, or null.</returns> /// <returns>The cache item, or null.</returns>
TCacheItem Get( TCacheItem Get(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -44,10 +46,12 @@ namespace Volo.Abp.Caching
/// if the related key not found in the cache. /// if the related key not found in the cache.
/// </summary> /// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param> /// <param name="keys">The keys of cached items to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>List of cache items.</returns> /// <returns>List of cache items.</returns>
KeyValuePair<TCacheKey, TCacheItem>[] GetMany( KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys, IEnumerable<TCacheKey> keys,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -60,11 +64,13 @@ namespace Volo.Abp.Caching
/// ///
/// </summary> /// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param> /// <param name="keys">The keys of cached items to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>List of cache items.</returns> /// <returns>List of cache items.</returns>
Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync( Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys, IEnumerable<TCacheKey> keys,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
@ -74,10 +80,12 @@ namespace Volo.Abp.Caching
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns> /// <returns>The cache item, or null.</returns>
Task<TCacheItem> GetAsync( Task<TCacheItem> GetAsync(
[NotNull] TCacheKey key, [NotNull] TCacheKey key,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
@ -89,12 +97,14 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param> /// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param> /// <param name="optionsFactory">The cache options for the factory delegate.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item.</returns> /// <returns>The cache item.</returns>
TCacheItem GetOrAdd( TCacheItem GetOrAdd(
TCacheKey key, TCacheKey key,
Func<TCacheItem> factory, Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -105,6 +115,7 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param> /// <param name="factory">The factory delegate is used to provide the cache item when no cache item is found for the given <paramref name="key" />.</param>
/// <param name="optionsFactory">The cache options for the factory delegate.</param> /// <param name="optionsFactory">The cache options for the factory delegate.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns> /// <returns>The cache item.</returns>
@ -112,6 +123,7 @@ namespace Volo.Abp.Caching
[NotNull] TCacheKey key, [NotNull] TCacheKey key,
Func<Task<TCacheItem>> factory, Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null, Func<DistributedCacheEntryOptions> optionsFactory = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
@ -122,11 +134,13 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param> /// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void Set( void Set(
TCacheKey key, TCacheKey key,
TCacheItem value, TCacheItem value,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -136,6 +150,7 @@ namespace Volo.Abp.Caching
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the cache.</param>
/// <param name="value">The cache item value to set in the cache.</param> /// <param name="value">The cache item value to set in the cache.</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <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> /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
@ -143,6 +158,7 @@ namespace Volo.Abp.Caching
[NotNull] TCacheKey key, [NotNull] TCacheKey key,
[NotNull] TCacheItem value, [NotNull] TCacheItem value,
[CanBeNull] DistributedCacheEntryOptions options = null, [CanBeNull] DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
@ -153,10 +169,12 @@ namespace Volo.Abp.Caching
/// </summary> /// </summary>
/// <param name="items">Items to set on the cache</param> /// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void SetMany( void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items, IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -166,12 +184,14 @@ namespace Volo.Abp.Caching
/// </summary> /// </summary>
/// <param name="items">Items to set on the cache</param> /// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param> /// <param name="options">The cache options for the value.</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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <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> /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task SetManyAsync( Task SetManyAsync(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items, IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null, DistributedCacheEntryOptions options = null,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );
@ -203,9 +223,11 @@ namespace Volo.Abp.Caching
/// Removes the cache item for given key from cache. /// Removes the cache item for given key from cache.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param> /// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void Remove( void Remove(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null bool? hideErrors = null
); );
@ -213,11 +235,13 @@ namespace Volo.Abp.Caching
/// Removes the cache item for given key from cache. /// Removes the cache item for given key from cache.
/// </summary> /// </summary>
/// <param name="key">The key of cached item to be retrieved from the cache.</param> /// <param name="key">The key of cached item to be retrieved from the 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="hideErrors">Indicates to throw or hide the exceptions for the distributed 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> /// <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> /// <returns>The <see cref="T:System.Threading.Tasks.Task" /> indicating that the operation is asynchronous.</returns>
Task RemoveAsync( Task RemoveAsync(
TCacheKey key, TCacheKey key,
bool considerUow = false,
bool? hideErrors = null, bool? hideErrors = null,
CancellationToken token = default CancellationToken token = default
); );

43
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItem.cs

@ -0,0 +1,43 @@
using System;
namespace Volo.Abp.Caching
{
[Serializable]
public class UnitOfWorkCacheItem<TValue>
where TValue : class
{
public bool IsRemoved { get; set; }
public TValue Value { get; set; }
public UnitOfWorkCacheItem()
{
}
public UnitOfWorkCacheItem(TValue value)
{
Value = value;
}
public UnitOfWorkCacheItem(TValue value, bool isRemoved)
{
Value = value;
IsRemoved = isRemoved;
}
public UnitOfWorkCacheItem<TValue> SetValue(TValue value)
{
Value = value;
IsRemoved = false;
return this;
}
public UnitOfWorkCacheItem<TValue> RemoveValue()
{
Value = null;
IsRemoved = true;
return this;
}
}
}

11
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/UnitOfWorkCacheItemExtensions.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.Caching
{
public static class UnitOfWorkCacheItemExtensions
{
public static TValue GetUnRemovedValueOrNull<TValue>(this UnitOfWorkCacheItem<TValue> item)
where TValue : class
{
return item != null && !item.IsRemoved ? item.Value : null;
}
}
}

2
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading; using System.Threading;

43
framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs

@ -1,4 +1,7 @@
using JetBrains.Annotations; using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.Abp.Uow namespace Volo.Abp.Uow
{ {
@ -10,5 +13,43 @@ namespace Volo.Abp.Uow
return unitOfWork.IsReserved && unitOfWork.ReservationName == reservationName; return unitOfWork.IsReserved && unitOfWork.ReservationName == reservationName;
} }
public static void AddItem<TValue>([NotNull] this IUnitOfWork unitOfWork, string key, TValue value)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
if (!unitOfWork.Items.ContainsKey(key))
{
unitOfWork.Items[key] = value;
}
else
{
unitOfWork.Items.Add(key, value);
}
}
public static TValue GetItemOrDefault<TValue>([NotNull] this IUnitOfWork unitOfWork, string key)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
return unitOfWork.Items.FirstOrDefault(x => x.Key == key).As<TValue>();
}
public static TValue GetOrAddItem<TValue>([NotNull] this IUnitOfWork unitOfWork, string key, Func<string, TValue> factory)
where TValue : class
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
return unitOfWork.Items.GetOrAdd(key, factory).As<TValue>();
}
public static void RemoveItem([NotNull] this IUnitOfWork unitOfWork, string key)
{
Check.NotNull(unitOfWork, nameof(unitOfWork));
unitOfWork.Items.RemoveAll(x => x.Key == key);
}
} }
} }

230
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Testing; using Volo.Abp.Testing;
using Volo.Abp.Uow;
using Xunit; using Xunit;
namespace Volo.Abp.Caching namespace Volo.Abp.Caching
@ -310,6 +311,235 @@ namespace Volo.Abp.Caching
cacheItem2.ShouldBeNull(); cacheItem2.ShouldBeNull();
} }
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_GetOrAddAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_GetOrAddAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
cacheValue = await personCache.GetOrAddAsync(key, () => Task.FromResult(new PersonCacheItem("john")), considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_SetAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_SetAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Only_Available_In_Uow_For_RemoveAsync()
{
const string key = "testkey";
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
var cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
await personCache.RemoveAsync(key, considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
uow.OnCompleted(async () =>
{
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
});
await uow.CompleteAsync();
}
}
[Fact]
public async Task Cache_Should_Rollback_With_Uow_For_RemoveAsync()
{
const string key = "testkey";
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{
await personCache.SetAsync(key, new PersonCacheItem("john"), considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldNotBeNull();
cacheValue.Name.ShouldBe("john");
await personCache.RemoveAsync(key, considerUow: true);
cacheValue = await personCache.GetAsync(key, considerUow: true);
cacheValue.ShouldBeNull();
}
cacheValue = await personCache.GetAsync(key, considerUow: false);
cacheValue.ShouldBeNull();
}
[Fact] [Fact]
public async Task Should_Set_And_Get_Multiple_Items_Async() public async Task Should_Set_And_Get_Multiple_Items_Async()
{ {

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs

@ -5,6 +5,7 @@ using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Uow;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement
{ {
@ -27,12 +28,14 @@ namespace Volo.Abp.FeatureManagement
FeatureDefinitionManager = featureDefinitionManager; FeatureDefinitionManager = featureDefinitionManager;
} }
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey) public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{ {
var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); var cacheItem = await GetCacheItemAsync(name, providerName, providerKey);
return cacheItem.Value; return cacheItem.Value;
} }
[UnitOfWork]
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) public virtual async Task SetAsync(string name, string value, string providerName, string providerKey)
{ {
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey);
@ -46,21 +49,25 @@ namespace Volo.Abp.FeatureManagement
featureValue.Value = value; featureValue.Value = value;
await FeatureValueRepository.UpdateAsync(featureValue); await FeatureValueRepository.UpdateAsync(featureValue);
} }
await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new FeatureValueCacheItem(featureValue?.Value), considerUow: true);
} }
[UnitOfWork]
public virtual async Task DeleteAsync(string name, string providerName, string providerKey) public virtual async Task DeleteAsync(string name, string providerName, string providerKey)
{ {
var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey); var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey);
foreach (var featureValue in featureValues) foreach (var featureValue in featureValues)
{ {
await FeatureValueRepository.DeleteAsync(featureValue); await FeatureValueRepository.DeleteAsync(featureValue);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
} }
} }
protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey) protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey)
{ {
var cacheKey = CalculateCacheKey(name, providerName, providerKey); var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey); var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null) if (cacheItem != null)
{ {
@ -103,7 +110,7 @@ namespace Volo.Abp.FeatureManagement
} }
} }
await Cache.SetManyAsync(cacheItems); await Cache.SetManyAsync(cacheItems, considerUow: true);
} }
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)

51
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Uow;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement
@ -14,11 +15,13 @@ namespace Volo.Abp.FeatureManagement
{ {
private IFeatureManagementStore FeatureManagementStore { get; set; } private IFeatureManagementStore FeatureManagementStore { get; set; }
private IFeatureValueRepository FeatureValueRepository { get; set; } private IFeatureValueRepository FeatureValueRepository { get; set; }
private IUnitOfWorkManager UnitOfWorkManager { get; set; }
protected FeatureManagementStore_Tests() protected FeatureManagementStore_Tests()
{ {
FeatureManagementStore = GetRequiredService<IFeatureManagementStore>(); FeatureManagementStore = GetRequiredService<IFeatureManagementStore>();
FeatureValueRepository = GetRequiredService<IFeatureValueRepository>(); FeatureValueRepository = GetRequiredService<IFeatureValueRepository>();
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
} }
[Fact] [Fact]
@ -73,6 +76,30 @@ namespace Volo.Abp.FeatureManagement
TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant()); TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant());
} }
[Fact]
public async Task Set_In_UnitOfWork_Should_Be_Consistent()
{
using (UnitOfWorkManager.Begin())
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(),
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBe(false.ToString().ToUpperInvariant());
}
}
[Fact] [Fact]
public async Task DeleteAsync() public async Task DeleteAsync()
{ {
@ -94,5 +121,29 @@ namespace Volo.Abp.FeatureManagement
} }
[Fact]
public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = UnitOfWorkManager.Begin())
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
await uow.SaveChangesAsync();
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull();
}
}
} }
} }

11
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManagementStore.cs

@ -5,6 +5,7 @@ using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Settings; using Volo.Abp.Settings;
using Volo.Abp.Uow;
namespace Volo.Abp.SettingManagement namespace Volo.Abp.SettingManagement
{ {
@ -27,11 +28,13 @@ namespace Volo.Abp.SettingManagement
SettingDefinitionManager = settingDefinitionManager; SettingDefinitionManager = settingDefinitionManager;
} }
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey) public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{ {
return (await GetCacheItemAsync(name, providerName, providerKey)).Value; return (await GetCacheItemAsync(name, providerName, providerKey)).Value;
} }
[UnitOfWork]
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) public virtual async Task SetAsync(string name, string value, string providerName, string providerKey)
{ {
var setting = await SettingRepository.FindAsync(name, providerName, providerKey); var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
@ -45,6 +48,8 @@ namespace Volo.Abp.SettingManagement
setting.Value = value; setting.Value = value;
await SettingRepository.UpdateAsync(setting); await SettingRepository.UpdateAsync(setting);
} }
await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new SettingCacheItem(setting?.Value), considerUow: true);
} }
public virtual async Task<List<SettingValue>> GetListAsync(string providerName, string providerKey) public virtual async Task<List<SettingValue>> GetListAsync(string providerName, string providerKey)
@ -53,19 +58,21 @@ namespace Volo.Abp.SettingManagement
return settings.Select(s => new SettingValue(s.Name, s.Value)).ToList(); return settings.Select(s => new SettingValue(s.Name, s.Value)).ToList();
} }
[UnitOfWork]
public virtual async Task DeleteAsync(string name, string providerName, string providerKey) public virtual async Task DeleteAsync(string name, string providerName, string providerKey)
{ {
var setting = await SettingRepository.FindAsync(name, providerName, providerKey); var setting = await SettingRepository.FindAsync(name, providerName, providerKey);
if (setting != null) if (setting != null)
{ {
await SettingRepository.DeleteAsync(setting); await SettingRepository.DeleteAsync(setting);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
} }
} }
protected virtual async Task<SettingCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey) protected virtual async Task<SettingCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey)
{ {
var cacheKey = CalculateCacheKey(name, providerName, providerKey); var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey); var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null) if (cacheItem != null)
{ {
@ -108,7 +115,7 @@ namespace Volo.Abp.SettingManagement
} }
} }
await Cache.SetManyAsync(cacheItems); await Cache.SetManyAsync(cacheItems, considerUow: true);
} }
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)

34
modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManagementStore_Tests.cs

@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Settings; using Volo.Abp.Settings;
using Volo.Abp.Uow;
using Xunit; using Xunit;
namespace Volo.Abp.SettingManagement namespace Volo.Abp.SettingManagement
@ -13,12 +14,14 @@ namespace Volo.Abp.SettingManagement
private readonly ISettingManagementStore _settingManagementStore; private readonly ISettingManagementStore _settingManagementStore;
private readonly ISettingRepository _settingRepository; private readonly ISettingRepository _settingRepository;
private readonly SettingTestData _testData; private readonly SettingTestData _testData;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public SettingManagementStore_Tests() public SettingManagementStore_Tests()
{ {
_settingManagementStore = GetRequiredService<ISettingManagementStore>(); _settingManagementStore = GetRequiredService<ISettingManagementStore>();
_settingRepository = GetRequiredService<ISettingRepository>(); _settingRepository = GetRequiredService<ISettingRepository>();
_testData = GetRequiredService<SettingTestData>(); _testData = GetRequiredService<SettingTestData>();
_unitOfWorkManager= GetRequiredService<IUnitOfWorkManager>();
} }
[Fact] [Fact]
@ -50,6 +53,21 @@ namespace Volo.Abp.SettingManagement
(await _settingRepository.FindAsync(_testData.SettingId)).Value.ShouldBe("43"); (await _settingRepository.FindAsync(_testData.SettingId)).Value.ShouldBe("43");
} }
[Fact]
public async Task Set_In_UnitOfWork_Should_Be_Consistent()
{
using (_unitOfWorkManager.Begin())
{
var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
value.ShouldBe("42");
await _settingManagementStore.SetAsync("MySetting1", "43", GlobalSettingValueProvider.ProviderName, null);
var valueAfterSet = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
valueAfterSet.ShouldBe("43");
}
}
[Fact] [Fact]
public async Task DeleteAsync() public async Task DeleteAsync()
{ {
@ -60,5 +78,21 @@ namespace Volo.Abp.SettingManagement
(await _settingRepository.FindAsync(_testData.SettingId)).ShouldBeNull(); (await _settingRepository.FindAsync(_testData.SettingId)).ShouldBeNull();
} }
[Fact]
public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = _unitOfWorkManager.Begin())
{
(await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null)).ShouldNotBeNull();
await _settingManagementStore.DeleteAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
await uow.SaveChangesAsync();
var value = await _settingManagementStore.GetOrNullAsync("MySetting1", GlobalSettingValueProvider.ProviderName, null);
value.ShouldBeNull();
}
}
} }
} }

Loading…
Cancel
Save