No results found.
Try your search with a different keyword or use * as a wildcard.
DistributedCacheManager .cs
using System.Collections.Concurrent;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using Nop.Core.Configuration;
using Nop.Core.Infrastructure;
namespace Nop.Core.Caching;
///
/// Represents a base distributed cache
///
public abstract class DistributedCacheManager : CacheKeyService, IStaticCacheManager
{
#region Fields
///
/// Holds the keys known by this nopCommerce instance
///
protected readonly ICacheKeyManager _localKeyManager;
protected readonly IDistributedCache _distributedCache;
protected readonly IConcurrentCollection _concurrentCollection;
///
/// Holds ongoing acquisition tasks, used to avoid duplicating work
///
protected readonly ConcurrentDictionary>> _ongoing = new();
#endregion
#region Ctor
protected DistributedCacheManager(AppSettings appSettings,
IDistributedCache distributedCache,
ICacheKeyManager cacheKeyManager,
IConcurrentCollection concurrentCollection)
: base(appSettings)
{
_distributedCache = distributedCache;
_localKeyManager = cacheKeyManager;
_concurrentCollection = concurrentCollection;
}
#endregion
#region Utilities
///
/// Clear all data on this instance
///
/// A task that represents the asynchronous operation
protected virtual void ClearInstanceData()
{
_concurrentCollection.Clear();
_localKeyManager.Clear();
}
///
/// Remove items by cache key prefix
///
/// Cache key prefix
/// Parameters to create cache key prefix
/// The removed keys
protected virtual IEnumerable RemoveByPrefixInstanceData(string prefix, params object[] prefixParameters)
{
var keyPrefix = PrepareKeyPrefix(prefix, prefixParameters);
_concurrentCollection.Prune(keyPrefix, out _);
return _localKeyManager.RemoveByPrefix(keyPrefix);
}
///
/// Prepare cache entry options for the passed key
///
/// Cache key
/// Cache entry options
protected virtual DistributedCacheEntryOptions PrepareEntryOptions(CacheKey key)
{
//set expiration time for the passed cache key
return new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(key.CacheTime)
};
}
///
/// Add the specified key and object to the local cache
///
/// Key of cached item
/// Value for caching
protected virtual void SetLocal(string key, object value)
{
_concurrentCollection.Add(key, value);
_localKeyManager.AddKey(key);
}
///
/// Remove the value with the specified key from the cache
///
/// Cache key
protected virtual void RemoveLocal(string key)
{
_concurrentCollection.Remove(key);
_localKeyManager.RemoveKey(key);
}
///
/// Try get a cached item. If it's not in the cache yet, then return default object
///
/// Type of cached item
/// Cache key
protected virtual async Task<(bool isSet, T item)> TryGetItemAsync(string key)
{
var json = await _distributedCache.GetStringAsync(key);
return string.IsNullOrEmpty(json)
? (false, default)
: (true, item: JsonConvert.DeserializeObject(json));
}
///
/// Remove the value with the specified key from the cache
///
/// Cache key
/// Remove from instance
protected virtual async Task RemoveAsync(string key, bool removeFromInstance = true)
{
_ongoing.TryRemove(key, out _);
await _distributedCache.RemoveAsync(key);
if (!removeFromInstance)
return;
RemoveLocal(key);
}
#endregion
#region Methods
///
/// Remove the value with the specified key from the cache
///
/// Cache key
/// Parameters to create cache key
/// A task that represents the asynchronous operation
public async Task RemoveAsync(CacheKey cacheKey, params object[] cacheKeyParameters)
{
await RemoveAsync(PrepareKey(cacheKey, cacheKeyParameters).Key);
}
///
/// Get a cached item. If it's not in the cache yet, then load and cache it
///
/// Type of cached item
/// Cache key
/// Function to load item if it's not in the cache yet
///
/// A task that represents the asynchronous operation
/// The task result contains the cached value associated with the specified key
///
public async Task GetAsync(CacheKey key, Func> acquire)
{
if (_concurrentCollection.TryGetValue(key.Key, out var data))
return (T)data;
var lazy = _ongoing.GetOrAdd(key.Key, _ => new(async () => await acquire(), true));
var setTask = Task.CompletedTask;
try
{
if (lazy.IsValueCreated)
return (T)await lazy.Value;
var (isSet, item) = await TryGetItemAsync(key.Key);
if (!isSet)
{
item = (T)await lazy.Value;
if (key.CacheTime == 0 || item == null)
return item;
setTask = _distributedCache.SetStringAsync(
key.Key,
JsonConvert.SerializeObject(item),
PrepareEntryOptions(key));
}
SetLocal(key.Key, item);
return item;
}
finally
{
_ = setTask.ContinueWith(_ => _ongoing.TryRemove(new KeyValuePair>>(key.Key, lazy)));
}
}
///
/// Get a cached item. If it's not in the cache yet, then load and cache it
///
/// Type of cached item
/// Cache key
/// Function to load item if it's not in the cache yet
///
/// A task that represents the asynchronous operation
/// The task result contains the cached value associated with the specified key
///
public Task GetAsync(CacheKey key, Func acquire)
{
return GetAsync(key, () => Task.FromResult(acquire()));
}
public async Task GetAsync(CacheKey key, T defaultValue = default)
{
var value = await _distributedCache.GetStringAsync(key.Key);
return value != null
? JsonConvert.DeserializeObject(value)
: defaultValue;
}
///
/// Get a cached item as an instance, or null on a cache miss.
///
/// Cache key
///
/// A task that represents the asynchronous operation
/// The task result contains the cached value associated with the specified key, or null if none was found
///
public async Task GetAsync(CacheKey key)
{
return await GetAsync(key);
}
///
/// Add the specified key and object to the cache
///
/// Key of cached item
/// Value for caching
/// A task that represents the asynchronous operation
public async Task SetAsync(CacheKey key, T data)
{
if (data == null || (key?.CacheTime ?? 0) <= 0)
return;
var lazy = new Lazy>(() => Task.FromResult(data as object), true);
try
{
_ongoing.TryAdd(key.Key, lazy);
// await the lazy task in order to force value creation instead of directly setting data
// this way, other cache manager instances can access it while it is being set
SetLocal(key.Key, await lazy.Value);
await _distributedCache.SetStringAsync(key.Key, JsonConvert.SerializeObject(data), PrepareEntryOptions(key));
}
finally
{
_ongoing.TryRemove(new KeyValuePair>>(key.Key, lazy));
}
}
///
/// Remove items by cache key prefix
///
/// Cache key prefix
/// Parameters to create cache key prefix
/// A task that represents the asynchronous operation
public abstract Task RemoveByPrefixAsync(string prefix, params object[] prefixParameters);
///
/// Clear all cache data
///
/// A task that represents the asynchronous operation
public abstract Task ClearAsync();
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
{
GC.SuppressFinalize(this);
}
#endregion
}