Reloading the cache automatically once expired is ASP.NET MVC

Load values to in memory cache as soon as they expire in ASP.NET MVC

In memory caching is not a bullet proof way of caching data in an application, especially if this application is distributed. The lack of synchronization between nodes can cause you application to misbehave showing wrong data or even throwing exceptions and crashing. This is one of the main reasons why distributed caching is the best way of doing the caching in your web application.

But not all applications you might be developing need to run as distributed on multiple machines. One of the example of this are intranet tools used inside organizations especially if these are not that big organizations and they do not suffer heavy load on daily basis. Spinning a REDIS cluster just for some administrative tool of setting up load balances and spinning VMs or containers is a bit of an overhead, don't you agree?

.NET Framework has build in caching as part of now a bit legacy System.Web library. But although it is considered as an old library you should avoid, if you are developing a web application for which you do not care much about scaling and you are sure you will run it on Windows machine, you should still use it as it has some great features out of the box you can use.

One of these is System.Web.HttpRuntime.Cache class. This class provides out of the box in-memory caching with some pretty cool features already built in. On of them is handling cache expiry for the cached values.

Usually you would use Quartz or some other library to run background tasks to pull data to your application and cache it. With HttpRuntime.Cache, you do not have to do this because it will do it for you.

Note

Another way of fetching data and caching is using Lazy pattern. .NET has even built in mechanism for Lazy pattern but this approach can cause requests pile up in cases of higher traffic or long running data fetch process. For that reason background data pulling for the caching purposes is more efficient

I assembled here a small class that can help refresh the cache as soon as the expiration period is over without having to involve any third party library like Quartz or write a background worker Task.

using System;
using System.Web;
using System.Web.Caching;

namespace WebApplication1
{
    public class SimpleMemoryCache<T> where T : class
    {
        private Func<T> dataFetch;
        private String key;
        private TimeSpan expire;


        public Func<T> DataFetch => this.dataFetch;

        public String Key => this.key;

        public T Value => HttpRuntime.Cache.Get(this.key) as T;

        public SimpleMemoryCache(String key, TimeSpan expire, Func<T> dataFetch)
        {
            this.key = key;
            this.expire = expire;
            this.dataFetch = dataFetch;

            addToCache();
        }


        private void addToCache()
        {
            HttpRuntime.Cache.Add(key, dataFetch(), null, DateTime.Now.AddTicks(expire.Ticks), TimeSpan.Zero, CacheItemPriority.Normal, cachedItemRemovedCallback);
        }

        private void cachedItemRemovedCallback(string key, Object val, CacheItemRemovedReason reason)
        {
            this.addToCache();
        }

    }
}
    

The class declares a callback method for onRemoveCallback parameter on Cache.Add method which has a signature of System.Web.Caching.CacheItemRemovedCallback delegate. This method will be invoked before the cache period expires, so fetching of the fresh content can be invoked inside it. 

To make things more generic and reusable as much as possible, data fetching is declared as a Func<T> and passed to the constructor. This Func<T> will be invoked for fetching the new data of generic type T. This way, you can use class to fetch pretty much any type of data and have direct cast provided by this wrapper class.

Demo

Now to test this mechanism and confirm that it works as expected, I created a small model, called it DummyModel which I will cache with expiry period of 10 seconds. This means new fres instance of the DummyModel will be fetched and cached.

using System;

namespace WebApplication1.Models
{
    public class DummyModel
    {
        public Guid ID { get; set; }
        public DateTime TimeCreated { get; set; }
    }
}
    

Just not to bang the head what to put as values for model properies I decided to use current time and new guid every time I create the instance. This could be done in the constructor, but since we are going to use our Func<T> to provide the logic for fetching the data, we'll leave the logic to be implemented inside it.

Since we want this class instance to exist though out the whole lifetime of the AppDomain, we will declare it as singleton. Just for the demo purposes I declared it in the controller as a static and initialized it with a anonymous method for Func<T> which returns an instance of generic type T which in our case is an instance of DummyModel class. 

using System;
using System.Web.Mvc;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        static SimpleMemoryCache<DummyModel> simpleCache = new SimpleMemoryCache<DummyModel>("MyCachedModelKey", TimeSpan.FromSeconds(10), ()=>
        {
            return new DummyModel()
            {
                ID = Guid.NewGuid(),
                TimeCreated = DateTime.Now
            };
        });

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}
    

No when you run the code and you put the breakpoint on return line, you will notice that this line is hit every 10 seconds, which is our exact time of cache expiration. You can see that cache fetching method is hit every time cache was about to expire providing the new value and caching it.

All that is needed to use the value is to get it from the Value property of the SimpleMemoryCache class instance.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform including ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article