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. 546
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  3. 34
      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. 4
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  7. 45
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkExtensions.cs
  8. 244
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/DistributedCache_Tests.cs
  9. 29
      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. 31
      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

546
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( hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
NormalizeKey(key),
Serializer.Serialize(value), try
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{ {
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,40 +670,73 @@ namespace Volo.Abp.Caching
SetManyFallback( SetManyFallback(
itemsArray, itemsArray,
options, options,
considerUow,
hideErrors hideErrors
); );
return; return;
} }
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try void SetRealCache()
{ {
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;
try try
{ {
foreach (var item in items) foreach (var item in items)
@ -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,38 +777,67 @@ namespace Volo.Abp.Caching
await SetManyFallbackAsync( await SetManyFallbackAsync(
itemsArray, itemsArray,
options, options,
considerUow,
hideErrors, hideErrors,
token token
); );
return; return;
} }
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try async Task SetRealCache()
{ {
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();
} }
} }
@ -730,7 +1033,7 @@ namespace Volo.Abp.Caching
{ {
AsyncHelper.RunSync(() => HandleExceptionAsync(ex)); AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
} }
protected virtual async Task HandleExceptionAsync(Exception ex) protected virtual async Task HandleExceptionAsync(Exception ex)
{ {
Logger.LogException(ex, LogLevel.Warning); Logger.LogException(ex, LogLevel.Warning);
@ -742,7 +1045,7 @@ namespace Volo.Abp.Caching
.NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning)); .NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));
} }
} }
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys) protected virtual KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys)
{ {
if (itemBytes.Length != itemKeys.Length) if (itemBytes.Length != itemKeys.Length)
@ -764,7 +1067,7 @@ namespace Volo.Abp.Caching
return result.ToArray(); return result.ToArray();
} }
[CanBeNull] [CanBeNull]
protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes) protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes)
{ {
@ -786,12 +1089,33 @@ namespace Volo.Abp.Caching
) )
).ToArray(); ).ToArray();
} }
private static KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItemsWithDefaultValues(TCacheKey[] keys) private static KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItemsWithDefaultValues(TCacheKey[] keys)
{ {
return keys return keys
.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>>());
}
} }
} }

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

@ -29,42 +29,48 @@ 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
); );
/// <summary> /// <summary>
/// Gets multiple cache items with the given keys. /// Gets multiple cache items with the given keys.
/// ///
/// The returned list contains exactly the same count of items specified in the given keys. /// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value /// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache. /// 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
); );
/// <summary> /// <summary>
/// Gets multiple cache items with the given keys. /// Gets multiple cache items with the given keys.
/// ///
/// The returned list contains exactly the same count of items specified in the given keys. /// The returned list contains exactly the same count of items specified in the given keys.
/// An item in the return list can not be null, but an item in the list has null value /// An item in the return list can not be null, but an item in the list has null value
/// if the related key not found in the cache. /// 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>
/// /// <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,25 +169,29 @@ 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
); );
/// <summary> /// <summary>
/// Sets multiple cache items. /// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually. /// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </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;
}
}
}

4
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;
@ -316,4 +316,4 @@ namespace Volo.Abp.Uow
return $"[UnitOfWork {Id}]"; return $"[UnitOfWork {Id}]";
} }
} }
} }

45
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);
}
} }
} }

244
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
@ -30,7 +31,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull(); cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName); cacheItem.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cacheKey); await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed) //Get (not exists since removed)
@ -111,7 +112,7 @@ namespace Volo.Abp.Caching
cacheItem1.ShouldNotBeNull(); cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName); cacheItem1.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cacheKey); await personCache.RemoveAsync(cacheKey);
@ -145,7 +146,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull(); cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName); cacheItem.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cacheKey); await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed) //Get (not exists since removed)
@ -227,7 +228,7 @@ namespace Volo.Abp.Caching
cacheItem1.ShouldNotBeNull(); cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName); cacheItem1.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cacheKey); await personCache.RemoveAsync(cacheKey);
@ -261,7 +262,7 @@ namespace Volo.Abp.Caching
cacheItem.ShouldNotBeNull(); cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName); cacheItem.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cacheKey); await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed) //Get (not exists since removed)
@ -299,7 +300,7 @@ namespace Volo.Abp.Caching
cacheItem2.ShouldNotBeNull(); cacheItem2.ShouldNotBeNull();
cacheItem2.Name.ShouldBe(personName); cacheItem2.Name.ShouldBe(personName);
//Remove //Remove
await personCache.RemoveAsync(cache1Key); await personCache.RemoveAsync(cache1Key);
await personCache.RemoveAsync(cache2Key); await personCache.RemoveAsync(cache2Key);
@ -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()
{ {
@ -340,4 +570,4 @@ namespace Volo.Abp.Caching
(await personCache.GetAsync("baris")).ShouldBeNull(); (await personCache.GetAsync("baris")).ShouldBeNull();
} }
} }
} }

29
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)
{ {
@ -68,28 +75,28 @@ namespace Volo.Abp.FeatureManagement
} }
cacheItem = new FeatureValueCacheItem(null); cacheItem = new FeatureValueCacheItem(null);
await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); await SetCacheItemsAsync(providerName, providerKey, name, cacheItem);
return cacheItem; return cacheItem;
} }
private async Task SetCacheItemsAsync( private async Task SetCacheItemsAsync(
string providerName, string providerName,
string providerKey, string providerKey,
string currentName, string currentName,
FeatureValueCacheItem currentCacheItem) FeatureValueCacheItem currentCacheItem)
{ {
var featureDefinitions = FeatureDefinitionManager.GetAll(); var featureDefinitions = FeatureDefinitionManager.GetAll();
var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey)) var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value); .ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>(); var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>();
foreach (var featureDefinition in featureDefinitions) foreach (var featureDefinition in featureDefinitions)
{ {
var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name); var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name);
cacheItems.Add( cacheItems.Add(
new KeyValuePair<string, FeatureValueCacheItem>( new KeyValuePair<string, FeatureValueCacheItem>(
CalculateCacheKey(featureDefinition.Name, providerName, providerKey), CalculateCacheKey(featureDefinition.Name, providerName, providerKey),
@ -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();
}
}
} }
} }

31
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
{ {
@ -16,8 +17,8 @@ namespace Volo.Abp.SettingManagement
protected IGuidGenerator GuidGenerator { get; } protected IGuidGenerator GuidGenerator { get; }
public SettingManagementStore( public SettingManagementStore(
ISettingRepository settingRepository, ISettingRepository settingRepository,
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
IDistributedCache<SettingCacheItem> cache, IDistributedCache<SettingCacheItem> cache,
ISettingDefinitionManager settingDefinitionManager) ISettingDefinitionManager settingDefinitionManager)
{ {
@ -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)
{ {
@ -75,26 +82,26 @@ namespace Volo.Abp.SettingManagement
cacheItem = new SettingCacheItem(null); cacheItem = new SettingCacheItem(null);
await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); await SetCacheItemsAsync(providerName, providerKey, name, cacheItem);
return cacheItem; return cacheItem;
} }
private async Task SetCacheItemsAsync( private async Task SetCacheItemsAsync(
string providerName, string providerName,
string providerKey, string providerKey,
string currentName, string currentName,
SettingCacheItem currentCacheItem) SettingCacheItem currentCacheItem)
{ {
var settingDefinitions = SettingDefinitionManager.GetAll(); var settingDefinitions = SettingDefinitionManager.GetAll();
var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey)) var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value); .ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, SettingCacheItem>>(); var cacheItems = new List<KeyValuePair<string, SettingCacheItem>>();
foreach (var settingDefinition in settingDefinitions) foreach (var settingDefinition in settingDefinitions)
{ {
var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name);
cacheItems.Add( cacheItems.Add(
new KeyValuePair<string, SettingCacheItem>( new KeyValuePair<string, SettingCacheItem>(
CalculateCacheKey(settingDefinition.Name, providerName, providerKey), CalculateCacheKey(settingDefinition.Name, providerName, providerKey),
@ -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