Merge pull request #1913 from gterdem/pr/1900

Added IDistributedCache<TCacheItem, TCacheKey>
pull/1957/head
Halil İbrahim Kalkan 5 years ago committed by GitHub
commit 82c01a4c95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,6 +21,7 @@ namespace Volo.Abp.Caching
context.Services.AddDistributedMemoryCache();
context.Services.AddSingleton(typeof(IDistributedCache<>), typeof(DistributedCache<>));
context.Services.AddSingleton(typeof(IDistributedCache<,>), typeof(DistributedCache<,>));
context.Services.Configure<AbpDistributedCacheOptions>(cacheOptions =>
{

@ -15,10 +15,36 @@ namespace Volo.Abp.Caching
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// </summary>
/// <typeparam name="TCacheItem">The type of cache item being cached.</typeparam>
public class DistributedCache<TCacheItem> : IDistributedCache<TCacheItem>
public class DistributedCache<TCacheItem> : DistributedCache<TCacheItem, string>, IDistributedCache<TCacheItem>
where TCacheItem : class
{
public ILogger<DistributedCache<TCacheItem>> Logger { get; set; }
public DistributedCache(
IOptions<CacheOptions> cacheOption,
IOptions<DistributedCacheOptions> distributedCacheOption,
IDistributedCache cache,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheSerializer serializer,
ICurrentTenant currentTenant) : base(
cacheOption: cacheOption,
distributedCacheOption: distributedCacheOption,
cache: cache,
cancellationTokenProvider: cancellationTokenProvider,
serializer: serializer,
currentTenant: currentTenant)
{
}
}
/// <summary>
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// Uses a generic cache key type of <typeparamref name="TCacheKey" /> type.
/// </summary>
/// <typeparam name="TCacheItem">The type of cache item being cached.</typeparam>
/// <typeparam name="TCacheKey">The type of cache key being used.</typeparam>
public class DistributedCache<TCacheItem, TCacheKey> : IDistributedCache<TCacheItem, TCacheKey>
where TCacheItem : class
{
public ILogger<DistributedCache<TCacheItem, TCacheKey>> Logger { get; set; }
protected string CacheName { get; set; }
@ -52,7 +78,7 @@ namespace Volo.Abp.Caching
_cacheOption = cacheOption.Value;
Cache = cache;
CancellationTokenProvider = cancellationTokenProvider;
Logger = NullLogger<DistributedCache<TCacheItem>>.Instance;
Logger = NullLogger<DistributedCache<TCacheItem, TCacheKey>>.Instance;
Serializer = serializer;
CurrentTenant = currentTenant;
@ -60,7 +86,41 @@ namespace Volo.Abp.Caching
SetDefaultOptions();
}
protected virtual string NormalizeKey(TCacheKey key)
{
var normalizedKey = "c:" + CacheName + ",k:" + _cacheOption.KeyPrefix + key.ToString();
if (!IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
{
normalizedKey = "t:" + CurrentTenant.Id.Value + "," + normalizedKey;
}
return normalizedKey;
}
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
{
foreach (var configure in _cacheOption.CacheConfigurators)
{
var options = configure.Invoke(CacheName);
if (options != null)
{
return options;
}
}
return _cacheOption.GlobalCacheEntryOptions;
}
protected virtual void SetDefaultOptions()
{
CacheName = CacheNameAttribute.GetCacheName(typeof(TCacheItem));
//IgnoreMultiTenancy
IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
}
/// <summary>
/// Gets a cache item with the given key. If no cache item is found for the given key then returns null.
/// </summary>
@ -68,7 +128,7 @@ namespace Volo.Abp.Caching
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item, or null.</returns>
public virtual TCacheItem Get(
string key,
TCacheKey key,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -106,7 +166,7 @@ namespace Volo.Abp.Caching
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
public virtual async Task<TCacheItem> GetAsync(
string key,
TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default)
{
@ -139,7 +199,6 @@ namespace Volo.Abp.Caching
return Serializer.Deserialize<TCacheItem>(cachedBytes);
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
@ -149,8 +208,8 @@ namespace Volo.Abp.Caching
/// <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>
/// <returns>The cache item.</returns>
public TCacheItem GetOrAdd(
string key,
public virtual TCacheItem GetOrAdd(
TCacheKey key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null)
@ -175,7 +234,6 @@ namespace Volo.Abp.Caching
return value;
}
/// <summary>
/// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item
/// provided by <paramref name="factory" /> delegate and returns the provided cache item.
@ -186,8 +244,8 @@ namespace Volo.Abp.Caching
/// <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 cache item.</returns>
public async Task<TCacheItem> GetOrAddAsync(
string key,
public virtual async Task<TCacheItem> GetOrAddAsync(
TCacheKey key,
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
@ -214,7 +272,6 @@ namespace Volo.Abp.Caching
return value;
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -223,7 +280,7 @@ namespace Volo.Abp.Caching
/// <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>
public virtual void Set(
string key,
TCacheKey key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null)
@ -249,7 +306,6 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Sets the cache item value for the provided key.
/// </summary>
@ -260,7 +316,7 @@ namespace Volo.Abp.Caching
/// <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 SetAsync(
string key,
TCacheKey key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
@ -288,15 +344,14 @@ namespace Volo.Abp.Caching
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>
public virtual void Refresh(
string key,
bool? hideErrors = null)
TCacheKey key, bool?
hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -315,7 +370,6 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Refreshes the cache value of the given key, and resets its sliding expiration timeout.
/// </summary>
@ -324,7 +378,7 @@ namespace Volo.Abp.Caching
/// <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(
string key,
TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default)
{
@ -345,15 +399,13 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <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(
string key,
TCacheKey key,
bool? hideErrors = null)
{
hideErrors = hideErrors ?? _distributedCacheOption.HideErrors;
@ -372,7 +424,6 @@ namespace Volo.Abp.Caching
throw;
}
}
/// <summary>
/// Removes the cache item for given key from cache.
/// </summary>
@ -381,7 +432,7 @@ namespace Volo.Abp.Caching
/// <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(
string key,
TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default)
{
@ -403,40 +454,6 @@ namespace Volo.Abp.Caching
}
}
protected virtual string NormalizeKey(string key)
{
var normalizedKey = "c:" + CacheName + ",k:" + _cacheOption.KeyPrefix + key;
if (!IgnoreMultiTenancy && CurrentTenant.Id.HasValue)
{
normalizedKey = "t:" + CurrentTenant.Id.Value + "," + normalizedKey;
}
return normalizedKey;
}
protected virtual DistributedCacheEntryOptions GetDefaultCacheEntryOptions()
{
foreach (var configure in _cacheOption.CacheConfigurators)
{
var options = configure.Invoke(CacheName);
if (options != null)
{
return options;
}
}
return _cacheOption.GlobalCacheEntryOptions;
}
protected virtual void SetDefaultOptions()
{
CacheName = CacheNameAttribute.GetCacheName(typeof(TCacheItem));
//IgnoreMultiTenancy
IgnoreMultiTenancy = typeof(TCacheItem).IsDefined(typeof(IgnoreMultiTenancyAttribute), true);
//Configure default cache entry options
DefaultCacheOptions = GetDefaultCacheEntryOptions();
}
}
}

@ -10,7 +10,18 @@ namespace Volo.Abp.Caching
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// </summary>
/// <typeparam name="TCacheItem">The type of cache item being cached.</typeparam>
public interface IDistributedCache<TCacheItem>
public interface IDistributedCache<TCacheItem> : IDistributedCache<TCacheItem, string>
where TCacheItem : class
{
}
/// <summary>
/// Represents a distributed cache of <typeparamref name="TCacheItem" /> type.
/// Uses a generic cache key type of <typeparamref name="TCacheKey" /> type.
/// </summary>
/// <typeparam name="TCacheItem">The type of cache item being cached.</typeparam>
/// <typeparam name="TCacheKey">The type of cache key being used.</typeparam>
public interface IDistributedCache<TCacheItem, TCacheKey>
where TCacheItem : class
{
/// <summary>
@ -20,7 +31,7 @@ namespace Volo.Abp.Caching
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item, or null.</returns>
TCacheItem Get(
string key,
TCacheKey key,
bool? hideErrors = null
);
@ -32,7 +43,7 @@ namespace Volo.Abp.Caching
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item, or null.</returns>
Task<TCacheItem> GetAsync(
[NotNull] string key,
[NotNull] TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default
);
@ -47,7 +58,7 @@ namespace Volo.Abp.Caching
/// <param name="hideErrors">Indicates to throw or hide the exceptions for the distributed cache.</param>
/// <returns>The cache item.</returns>
TCacheItem GetOrAdd(
string key,
TCacheKey key,
Func<TCacheItem> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null
@ -64,7 +75,7 @@ namespace Volo.Abp.Caching
/// <param name="token">The <see cref="T:System.Threading.CancellationToken" /> for the task.</param>
/// <returns>The cache item.</returns>
Task<TCacheItem> GetOrAddAsync(
[NotNull] string key,
[NotNull] TCacheKey key,
Func<Task<TCacheItem>> factory,
Func<DistributedCacheEntryOptions> optionsFactory = null,
bool? hideErrors = null,
@ -79,7 +90,7 @@ namespace Volo.Abp.Caching
/// <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 Set(
string key,
TCacheKey key,
TCacheItem value,
DistributedCacheEntryOptions options = null,
bool? hideErrors = null
@ -95,7 +106,7 @@ namespace Volo.Abp.Caching
/// <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 SetAsync(
[NotNull] string key,
[NotNull] TCacheKey key,
[NotNull] TCacheItem value,
[CanBeNull] DistributedCacheEntryOptions options = null,
bool? hideErrors = null,
@ -108,7 +119,7 @@ namespace Volo.Abp.Caching
/// <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>
void Refresh(
string key,
TCacheKey key,
bool? hideErrors = null
);
@ -120,7 +131,7 @@ namespace Volo.Abp.Caching
/// <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 RefreshAsync(
string key,
TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default
);
@ -131,7 +142,7 @@ namespace Volo.Abp.Caching
/// <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>
void Remove(
string key,
TCacheKey key,
bool? hideErrors = null
);
@ -143,7 +154,7 @@ namespace Volo.Abp.Caching
/// <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 RemoveAsync(
string key,
TCacheKey key,
bool? hideErrors = null,
CancellationToken token = default
);

@ -122,5 +122,192 @@ namespace Volo.Abp.Caching
cacheItem1.ShouldNotBeNull();
}
[Fact]
public async Task Should_Set_Get_And_Remove_Cache_Items_With_Integer_Type_CacheKey()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem, int>>();
var cacheKey = 42;
const string personName = "john nash";
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
//Set
cacheItem = new PersonCacheItem(personName);
await personCache.SetAsync(cacheKey, cacheItem);
//Get (it should be available now
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
}
[Fact]
public async Task GetOrAddAsync_With_Integer_Type_CacheKey()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem, int>>();
var cacheKey = 42;
const string personName = "john nash";
//Will execute the factory method to create the cache item
bool factoryExecuted = false;
var cacheItem = await personCache.GetOrAddAsync(cacheKey,
async () =>
{
factoryExecuted = true;
return new PersonCacheItem(personName);
});
factoryExecuted.ShouldBeTrue();
cacheItem.Name.ShouldBe(personName);
//This time, it will not execute the factory
factoryExecuted = false;
cacheItem = await personCache.GetOrAddAsync(cacheKey,
async () =>
{
factoryExecuted = true;
return new PersonCacheItem(personName);
});
factoryExecuted.ShouldBeFalse();
cacheItem.Name.ShouldBe(personName);
}
[Fact]
public async Task SameClassName_But_DiffNamespace_Should_Not_Use_Same_Cache_With_Integer_CacheKey()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem, int>>();
var otherPersonCache = GetRequiredService<IDistributedCache<Sail.Testing.Caching.PersonCacheItem, int>>();
var cacheKey = 42;
const string personName = "john nash";
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
var cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//Set
cacheItem = new PersonCacheItem(personName);
await personCache.SetAsync(cacheKey, cacheItem);
//Get (it should be available now, but otherPersonCache not exists now.
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldBeNull();
//set other person cache
cacheItem1 = new Sail.Testing.Caching.PersonCacheItem(personName);
await otherPersonCache.SetAsync(cacheKey, cacheItem1);
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
cacheItem1 = await otherPersonCache.GetAsync(cacheKey);
cacheItem1.ShouldNotBeNull();
}
[Fact]
public async Task Should_Set_Get_And_Remove_Cache_Items_With_Object_Type_CacheKey()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem, ComplexObjectAsCacheKey>>();
var cacheKey = new ComplexObjectAsCacheKey { Name = "DummyData", Age = 42 };
const string personName = "john nash";
//Get (not exists yet)
var cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
//Set
cacheItem = new PersonCacheItem(personName);
await personCache.SetAsync(cacheKey, cacheItem);
//Get (it should be available now
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldNotBeNull();
cacheItem.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cacheKey);
//Get (not exists since removed)
cacheItem = await personCache.GetAsync(cacheKey);
cacheItem.ShouldBeNull();
}
[Fact]
public async Task Should_Set_Get_And_Remove_Cache_Items_For_Same_Object_Type_With_Different_CacheKeys()
{
var personCache = GetRequiredService<IDistributedCache<PersonCacheItem, ComplexObjectAsCacheKey>>();
var cache1Key = new ComplexObjectAsCacheKey { Name = "John", Age = 42 };
var cache2Key = new ComplexObjectAsCacheKey { Name = "Jenny", Age = 24 };
const string personName = "john nash";
//Get (not exists yet)
var cacheItem1 = await personCache.GetAsync(cache1Key);
var cacheItem2 = await personCache.GetAsync(cache2Key);
cacheItem1.ShouldBeNull();
cacheItem2.ShouldBeNull();
//Set
cacheItem1 = new PersonCacheItem(personName);
cacheItem2 = new PersonCacheItem(personName);
await personCache.SetAsync(cache1Key, cacheItem1);
await personCache.SetAsync(cache2Key, cacheItem2);
//Get (it should be available now
cacheItem1 = await personCache.GetAsync(cache1Key);
cacheItem1.ShouldNotBeNull();
cacheItem1.Name.ShouldBe(personName);
cacheItem2 = await personCache.GetAsync(cache2Key);
cacheItem2.ShouldNotBeNull();
cacheItem2.Name.ShouldBe(personName);
//Remove
await personCache.RemoveAsync(cache1Key);
await personCache.RemoveAsync(cache2Key);
//Get (not exists since removed)
cacheItem1 = await personCache.GetAsync(cache1Key);
cacheItem1.ShouldBeNull();
cacheItem2 = await personCache.GetAsync(cache2Key);
cacheItem2.ShouldBeNull();
}
}
}

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace Volo.Abp.Caching
{
@ -17,7 +18,33 @@ namespace Volo.Abp.Caching
Name = name;
}
}
public class ComplexObjectAsCacheKey
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
// Return selective fields
//return $"{Name}_{Age}";
// Return all the fields concatenated
var sb = new System.Text.StringBuilder();
var properties = this.GetType().GetProperties()
.Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(this, null);
if (value != null)
{
sb.Append(value.ToString());
}
}
return sb.ToString();
}
}
}
namespace Sail.Testing.Caching
{
[Serializable]

Loading…
Cancel
Save