Archive for May, 2017

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))}
    });

Tags: ,

Wednesday, May 24th, 2017 Uncategorized No Comments