Resolved #4482 Add SetMany and GetMany to the IDistributedCache

pull/4493/head
Halil İbrahim Kalkan 5 years ago
parent a4cdb76249
commit 8d3a145b7b

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -28,16 +31,16 @@ namespace Volo.Abp.Caching
IDistributedCacheSerializer serializer,
IDistributedCacheKeyNormalizer keyNormalizer,
IHybridServiceScopeFactory serviceScopeFactory) : base(
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory)
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
keyNormalizer: keyNormalizer,
serviceScopeFactory: serviceScopeFactory)
{
}
}
/// <summary>
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// Uses a generic cache key type of <typeparamref name="TCacheKey" /> type.
@ -148,19 +151,151 @@ namespace Volo.Abp.Caching
{
if (hideErrors == true)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
HandleException(ex);
return null;
}
throw;
}
if (cachedBytes == null)
return ToCacheItem(cachedBytes);
}
public virtual KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null)
{
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
return null;
return GetManyFallback(
keyArray,
hideErrors
);
}
return Serializer.Deserialize<TCacheItem>(cachedBytes);
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
try
{
cachedBytes = cacheSupportsMultipleItems.GetMany(keyArray.Select(NormalizeKey));
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return ToCacheItemsWithDefaultValues(keyArray);
}
throw;
}
return ToCacheItems(cachedBytes, keyArray);
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] GetManyFallback(
TCacheKey[] keys,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(
key,
Get(key, hideErrors: false)
)
).ToArray();
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return ToCacheItemsWithDefaultValues(keys);
}
throw;
}
}
public virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
CancellationToken token = default)
{
var keyArray = keys.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
return await GetManyFallbackAsync(
keyArray,
hideErrors,
token
);
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
byte[][] cachedBytes;
try
{
cachedBytes = await cacheSupportsMultipleItems.GetManyAsync(
keyArray.Select(NormalizeKey),
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues(keyArray);
}
throw;
}
return ToCacheItems(cachedBytes, keyArray);
}
protected virtual async Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyFallbackAsync(
TCacheKey[] keys,
bool? hideErrors = null,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
var result = new List<KeyValuePair<TCacheKey, TCacheItem>>();
foreach (var key in keys)
{
result.Add(new KeyValuePair<TCacheKey, TCacheItem>(
key,
await GetAsync(key, false, token))
);
}
return result.ToArray();
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return ToCacheItemsWithDefaultValues(keys);
}
throw;
}
}
/// <summary>
@ -307,7 +442,7 @@ namespace Volo.Abp.Caching
{
if (hideErrors == true)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
HandleException(ex);
return;
}
@ -354,14 +489,156 @@ 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 void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
{
var itemsArray = items.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
SetManyFallback(
itemsArray,
options,
hideErrors
);
return;
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
cacheSupportsMultipleItems.SetMany(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
protected virtual void SetManyFallback(
KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
foreach (var item in items)
{
Set(
item.Key,
item.Value,
options: options,
hideErrors: false
);
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
HandleException(ex);
return;
}
throw;
}
}
public virtual async Task SetManyAsync(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default)
{
var itemsArray = items.ToArray();
var cacheSupportsMultipleItems = Cache as ICacheSupportsMultipleItems;
if (cacheSupportsMultipleItems == null)
{
await SetManyFallbackAsync(
itemsArray,
options,
hideErrors,
token
);
return;
}
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
await cacheSupportsMultipleItems.SetManyAsync(
ToRawCacheItems(itemsArray),
options ?? DefaultCacheOptions,
CancellationTokenProvider.FallbackToProvider(token)
);
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
protected virtual async Task SetManyFallbackAsync(
KeyValuePair<TCacheKey, TCacheItem>[] items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
try
{
foreach (var item in items)
{
await SetAsync(
item.Key,
item.Value,
options: options,
hideErrors: false,
token: token
);
}
}
catch (Exception ex)
{
if (hideErrors == true)
{
await HandleExceptionAsync(ex);
return;
}
throw;
}
}
public virtual void Refresh(
TCacheKey key, bool?
hideErrors = null)
hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -373,20 +650,14 @@ namespace Volo.Abp.Caching
{
if (hideErrors == true)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
HandleException(ex);
return;
}
throw;
}
}
/// <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(
TCacheKey key,
bool? hideErrors = null,
@ -410,11 +681,6 @@ 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="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
public virtual void Remove(
TCacheKey key,
bool? hideErrors = null)
@ -429,7 +695,7 @@ namespace Volo.Abp.Caching
{
if (hideErrors == true)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
HandleException(ex);
return;
}
@ -437,13 +703,6 @@ 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="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(
TCacheKey key,
bool? hideErrors = null,
@ -467,6 +726,11 @@ namespace Volo.Abp.Caching
}
}
protected virtual void HandleException(Exception ex)
{
AsyncHelper.RunSync(() => HandleExceptionAsync(ex));
}
protected virtual async Task HandleExceptionAsync(Exception ex)
{
Logger.LogException(ex, LogLevel.Warning);
@ -478,5 +742,56 @@ namespace Volo.Abp.Caching
.NotifyAsync(new ExceptionNotificationContext(ex, LogLevel.Warning));
}
}
protected virtual KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItems(byte[][] itemBytes, TCacheKey[] itemKeys)
{
if (itemBytes.Length != itemKeys.Length)
{
throw new AbpException("count of the item bytes should be same with the count of the given keys");
}
var result = new List<KeyValuePair<TCacheKey, TCacheItem>>();
for (int i = 0; i < itemKeys.Length; i++)
{
result.Add(
new KeyValuePair<TCacheKey, TCacheItem>(
itemKeys[i],
ToCacheItem(itemBytes[i])
)
);
}
return result.ToArray();
}
[CanBeNull]
protected virtual TCacheItem ToCacheItem([CanBeNull] byte[] bytes)
{
if (bytes == null)
{
return null;
}
return Serializer.Deserialize<TCacheItem>(bytes);
}
protected virtual KeyValuePair<string, byte[]>[] ToRawCacheItems(KeyValuePair<TCacheKey, TCacheItem>[] items)
{
return items
.Select(i => new KeyValuePair<string, byte[]>(
NormalizeKey(i.Key),
Serializer.Serialize(i.Value)
)
).ToArray();
}
private static KeyValuePair<TCacheKey, TCacheItem>[] ToCacheItemsWithDefaultValues(TCacheKey[] keys)
{
return keys
.Select(key => new KeyValuePair<TCacheKey, TCacheItem>(key, default))
.ToArray();
}
}
}

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
namespace Volo.Abp.Caching
{
public interface ICacheSupportsMultipleItems
{
byte[][] GetMany(
IEnumerable<string> keys
);
Task<byte[][]> GetManyAsync(
IEnumerable<string> keys,
CancellationToken token = default
);
void SetMany(
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options
);
Task SetManyAsync(
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options,
CancellationToken token = default
);
}
}

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -34,6 +35,39 @@ namespace Volo.Abp.Caching
TCacheKey key,
bool? hideErrors = null
);
/// <summary>
/// Gets multiple cache items with 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
/// if the related key not found in the cache.
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>List of cache items.</returns>
KeyValuePair<TCacheKey, TCacheItem>[] GetMany(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null
);
/// <summary>
/// Gets multiple cache items with 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
/// if the related key not found in the cache.
///
/// </summary>
/// <param name="keys">The keys of cached items to be retrieved from the cache.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// /// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>List of cache items.</returns>
Task<KeyValuePair<TCacheKey, TCacheItem>[]> GetManyAsync(
IEnumerable<TCacheKey> keys,
bool? hideErrors = null,
CancellationToken token = default
);
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
@ -113,6 +147,35 @@ namespace Volo.Abp.Caching
CancellationToken token = default
);
/// <summary>
/// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</param>
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
void SetMany(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null
);
/// <summary>
/// Sets multiple cache items.
/// Based on the implementation, this can be more efficient than setting multiple items individually.
/// </summary>
/// <param name="items">Items to set on the cache</param>
/// <param name="options">The cache options for the value.</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 SetManyAsync(
IEnumerable<KeyValuePair<TCacheKey, TCacheItem>> items,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
CancellationToken token = default
);
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Testing;
@ -80,7 +81,6 @@ namespace Volo.Abp.Caching
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
var otherPersonCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem>>();
var cacheKey = Guid.NewGuid().ToString();
const string personName = "john nash";
@ -310,5 +310,34 @@ namespace Volo.Abp.Caching
cacheItem2.ShouldBeNull();
}
[Fact]
public async Task Should_Set_And_Get_Multiple_Items_Async()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem>>();
await personCache.SetManyAsync(new[]
{
new KeyValuePair<string, PersonCacheItem>("john", new PersonCacheItem("John Nash")),
new KeyValuePair<string, PersonCacheItem>("thomas", new PersonCacheItem("Thomas Moore"))
});
var cacheItems = await personCache.GetManyAsync(new[]
{
"john",
"thomas",
"baris" //doesn't exist
});
cacheItems.Length.ShouldBe(3);
cacheItems[0].Key.ShouldBe("john");
cacheItems[0].Value.Name.ShouldBe("John Nash");
cacheItems[1].Key.ShouldBe("thomas");
cacheItems[1].Value.Name.ShouldBe("Thomas Moore");
cacheItems[2].Key.ShouldBe("baris");
cacheItems[2].Value.ShouldBeNull();
(await personCache.GetAsync("john")).Name.ShouldBe("John Nash");
(await personCache.GetAsync("baris")).ShouldBeNull();
}
}
}
Loading…
Cancel
Save