Combine Sliding and Absolute Expiration in MemoryCache
I wanted to use both absolute and sliding expiration for my cached item. By default the MemoryCache Class does not support this. I found a good answer on StackOverFlow Combine Sliding and Absolute Expiration but I wanted more flexibility than that solution so I looked at creating my own implementation of the ChangeMonitor.
Using the TPL I thought it would be nice to also support it with a CancellationToken as well. I reviewed the source for the SqlChangeMonitor and it seemed pretty straight forward.
public class CancellationTokenChangeMonitor : ChangeMonitor
{
private readonly IList _disposers = new List();
public CancellationTokenChangeMonitor(CancellationToken token)
{
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
var shouldDispose = true;
try
{
_disposers.Add(token.Register(() => OnChanged(null)));
shouldDispose = false;
}
finally
{
InitializationComplete();
if (shouldDispose)
{
Dispose();
}
}
}
public CancellationTokenChangeMonitor(DateTimeOffset expiration)
{
UniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
var shouldDispose = true;
try
{
// if date is in the past then pass in zero
var expires = new[] {expiration - DateTimeOffset.UtcNow, TimeSpan.Zero}.Max();
var tokenSource = new CancellationTokenSource(expires);
_disposers.Add(tokenSource.Token.Register(() => OnChanged(null)));
_disposers.Add(tokenSource);
shouldDispose = false;
}
finally
{
InitializationComplete();
if (shouldDispose)
{
Dispose();
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var disposer in _disposers)
{
disposer?.Dispose();
}
}
}
public override string UniqueId { get; }
}
Created two overloads for the constructor. One that will take an existing CancellationToken and the other a DateTimeOffSet, to set the absolute expiration date. Now I can sliding and absolute or tie it directly to a CancellationToken
Example:
// Expire in 1 minute if no access
// or expire if cache is older than 1 hour
MemoryCache.Default.Add("NHail", "Charles N Rice", new CacheItemPolicy()
{
SlidingExpiration = TimeSpan.FromMinutes(1),
ChangeMonitors = {new CancellationTokenChangeMonitor(DateTimeOffset.UtcNow.AddHours(1))}
});