Browse Source

Add more "Many" methods to the ICacheSupportsMultipleItems

pull/7523/head
liangshiwei 6 years ago
parent
commit
e6b44e7330
  1. 38
      framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
  2. 281
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs
  3. 16
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs
  4. 63
      framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs
  5. 16
      framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs

38
framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs

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

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

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

16
framework/src/Volo.Abp.Caching/Volo/Abp/Caching/ICacheSupportsMultipleItems.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
@ -25,6 +26,15 @@ namespace Volo.Abp.Caching
IEnumerable<KeyValuePair<string, byte[]>> items, IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options, DistributedCacheEntryOptions options,
CancellationToken token = default CancellationToken token = default
); );
void RefreshMany(
IEnumerable<string> keys
);
Task RefreshManyAsync(
IEnumerable<string> keys,
CancellationToken token = default
);
} }
} }

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

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

16
framework/test/Volo.Abp.Caching.Tests/Volo/Abp/Caching/TestMemoryDistributedCache.cs

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

Loading…
Cancel
Save