There’s a worn joke within developer circles that states that there are two things that are really hard about development: naming things, cache invalidation, and off-by-one errors. I’ve always found that joke funny – simply because of how true it is. And, as great as caching support is in ASP.NET applications, it could use some serious help when it comes to cache invalidation. Furthermore, because these limitations are found in the .NET Framework, I’m hoping that as the ASP.NET Team continues to decouple ASP.NET 5 from the monolithic-ness of the .NET Framework that we might eventually see some cycles devoted to making caching that much better – eventually.
ASP.NET – Born to Cache
Once upon a time, ASP.NET was born – and it spilled out of the gate with fantastic, native, support for caching. Oh sure, there were a few warts – and you had to be careful with memory pressure and other concerns – but I’ve always maintained that one of the hands-down biggest strengths of ASP.NET has been it’s native, first-class, support for caching right out of the box.
Caching support in ASP.NET applications was actually so powerful that .NET developers working on non-web applications wanted to, rightly, take advantage of caching as well. Which, in turn, helped lead to the birth of System.Runtime.Caching in .NET 4.0 – or the ability to add caching functionality into .NET applications without taking a ‘hit’ (or dependency) on System.Web. Ironically, in the past few years as even ASP.NET has tried to divorce itself somewhat from the ‘bloat’ of System.Web – this has also meant that ASP.NET apps striving for greater compliance with OWIN standards have been able to ‘ditch’ System.Web.Caching in favor of System.Runtime.Caching as well. Only, all these years later, there are still some decent problems with either library.
Problems with System.Web.Caching
The biggest problem with the caching in System.Web is that, ironically, future versions of ASP.NET are striving for a much more modular approach to managing underlying framework resources and libraries – to the point where the idea is that developers will simply configure and use only the components that they need. Accordingly, using System.Web for caching in newer projects not only makes less and less sense – but it’s already become borderline useless or tedious to use in a number of more modern scenarios. As a case in point, assume you’ve built an ASP.NET MVC 5 application – with some sort of data repository class or object. Further, assume that you’ve wrapped System.Web’s caching functionality into some sort of ‘CacheStore’ class – or something similar – to abstract it away from your repository (or caching repository). Such a configuration will work fine IF you’re running fully synchronous operations in your ASP.NET MVC 5 application. But, if you start using async and await (or other asynchrony implementations), you’ll be running on ‘background’ threads that won’t natively have access to the HttpContext.Current.Cache implementation that you’ll need for accessing the System.Web cache containing your cached web app data. As such, when running asynchronous code, you’ll want to switch to System.Runtime.Caching – as it isn’t hard-coded to an HttpContext of any sort.
Problems with System.Runtime.Caching
Only, as great as it is to having caching that’s totally free of an HttpContext, System.Runtime.Caching feels like a work in progress. For example, since its introduction, System.Runtime.Caching has had what appears to be built-in support for cache sections or ‘regions’ – such that you could drop cached items into logical containers with ‘markers’ such as “marketing” or “UserID:227” to help make cache-invalidation insanely easy to tackle in certain scenarios. Sadly, though, while System.Runtime.Caching was introduced in .NET 4.0, as of .NET 4.6 this functionality is not supported – to the point where you’ll get a NotSupportedException if you try to make use of this interface at all. Instead, if you want to make use of this functionality, you have to wire up your own custom cache class. Sadly, for all of its benefits, System.Runtime.Caching also continues to suffer from some pretty ugly problems with memory management.
Personally, though, one of the things I find most lacking with System.Runtime.Caching is that there is simply no resource-friendly and/or thread-safe way to iterate over the items in cache. Not only does that mean that establishing instrumentation about what’s in the cache is tough and/or ugly, but techniques for trying to evict items from the cache based on cache-key conventions becomes exponentially harder – something that’s ugly and problematic given how essential proper cache invalidation is on high performance sites.
Why ASP.NET Developers Should Care
Simply put, ASP.NET’s native support for cache (from day one) is one of the things that has made ASP.NET so powerful and scalable (sites that properly leverage caching – even for seconds at a time – can run circles around sites that do not). As ASP.NET moves away from System.Web towards a more modular architecture, using System.Web for caching will not only be silly, but will stretch right into ugly when asynchrony is involved. Which leaves us with System.Runtime.Caching – and its ongoing limitations as well as the feeling that it’s not quite fully baked. Hopefully Microsoft can either find some developer cycles to fix/address/rewrite System.Runtime.Caching (or a replacement), or – my preference – maybe the ASP.NET Team will build a whole new caching library/module that’ll go hand-in-hand with ASP.NET 5’s modular architecture (once they’ve addressed some of the other items currently on their plate). Until then, we’re stuck with two caching options that both have some significant liabilities and limitations.