I am trying to refresh IMemoryCache
programmatically. After researching a few links
about Eviction Calback and Clearing cache, I thought I could combine the strategies i.e. clear the cache which would cause the eviction callback to fire. However apparently the post eviction callback won't trigger when the cache is cleared using reflection because it seems the whole cache item with its options (that includes the callback ) is gone. (cache item count goes to 0)
So my question is about refreshing a cache item before expiration, as this issue is still open
private static Dictionary<string, CancellationTokenSource> tokenDict = new Dictionary<string, CancellationTokenSource>();
private MemoryCacheEntryOptions CacheOptions
{
get
{
var expirationToken = new CancellationChangeToken( new CancellationTokenSource(TimeSpan.FromMinutes(ExpirationMinutes + .01)).Token);
var options = new MemoryCacheEntryOptions()
// Do not remove due to memory pressure
.SetPriority(Microsoft.Extensions.Caching.Memory.CacheItemPriority.NeverRemove)
.SetSlidingExpiration(TimeSpan.FromMinutes(ExpirationMinutes))
// Force eviction to run AT expriry, default eviction happens when item is requested after expiry
.AddExpirationToken(expirationToken)
.RegisterPostEvictionCallback(callback: CacheItemRemoved, state: this);
tokenDict[cacheKey] = cancellationTokenSource;
return options;
}
}
private void CacheItemRemoved(object key, object value, EvictionReason reason, object state)
{
_logger.LogInformation($"Reloading {key} cache upon eviction");
switch (key)
{
case AccountCacheKey:
GetAccountCacheAsync();
break;
case FundCacheKey:
GetFundCacheAsync();
break;
default:
break;
}
}
private async Task<List<Account>> GetAccountCacheAsync()
{
return await _cache.GetOrCreateAsync(AccountCacheKey, async entry =>
{
entry.SetOptions(CacheOptions);
var accounts = await LoadAccountsAsync().ConfigureAwait(false);
return accounts;
}).ConfigureAwait(false);
}
private async Task<List<Fund>> GetFundCacheAsync()
{
return await _cache.GetOrCreateAsync(FundCacheKey, async entry =>
{
entry.SetOptions(CacheOptions);
var funds = await LoadFundsAsync().ConfigureAwait(false);
return funds;
}).ConfigureAwait(false);
}
public async Task RefreshCacheAsync()
{
var cacheKeys = new List<string> { AccountCacheKey, FundCacheKey };
foreach (var key in cacheKeys)
{
if (tokenDict.TryGetValue(key, out var token))
{
if (token != null && !token.IsCancellationRequested && token.Token.CanBeCanceled)
{
token.Cancel();
token.Dispose();
}
}
}
}