LazyCache and the newer FusionCache are two great libraries that simplifies working with caching in ASP.Net Core. Something I’ve often been missing though is the ability to dispose of the entire cache, not just single items. (My understanding is that this limitation stems from them being built on top of the distributed cache in ASP.Net Core.) You could solve it by keeping track of all cached items, but that seems overkill. Let see how we can solve it in a simple way with some clever code!
Here is a typical way to use fetch an item from the database:
1 2 3 4 |
var product = await _cache.GetOrAddAsync($"product-{id}", async () => { return await _db.getProductById(id); }, TimeSpan.FromHours(1)); |
Notice $"product-{id}" which is the key by which the cache framework looks up data from the cache. Let’s add a helper class and move the key there instead:
1 2 3 4 5 6 7 8 9 |
public static class CacheHelper { public static string ProductKey = $"product-{DateTime.Now}"; public static void BustCache() { ProductKey = $"product-{DateTime.Now}"; } } |
Now we can use CacheHelper.ProductKey instead of the hard coded key:
1 2 3 4 |
var product = await _cache.GetOrAddAsync($"{CacheHelper.ProductKey}-{id}", async () => { return await _db.getProductById(id); }, TimeSpan.FromHours(1)); |
Now if we want to clear the cache we can simply call CacheHelper.BustCache() ! We’re not actually clearing the cache, just changing the identifiers and thus forcing the cache to repopulate. The old cache items will go stale and eventually be flushed by the API.
Let’s improve the code a bit more. It’s usually a good idea to put logic and business rules in one place. Composing the cache key manually ( $"{CacheHelper.ProductKey}-{id}" ) and setting the duration every time we want to get a product item sounds like an accident waiting to happen, so we’ll move everything to the CacheHelper class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public static class CacheHelper { public static string GetProductKey(int id) { return $"Product-{id}-{ProductCacheDate}".ToLower(); } public static void BustCache() { ProductCacheDate = DateTime.Now.ToString(); } private static string ProductCacheDate = DateTime.Now.ToString(); } public static class CacheDurations { public static TimeSpan Product = TimeSpan.FromHours(1); } |
Then to use it:
1 2 3 4 |
var product = await _cache.GetOrAddAsync(CacheHelper.GetProductKey(id), async () => { return await _db.getProductById(id); }, CacheDurations.Product); |
CacheHelper.GetProductKey(id) makes it easy to get the correctly formatted key whenever we need it. For more advanced keys we can even add multiple parameters to the method. And when someone asks how long items are cached you can quickly look it up! This could also be a good place to add some comments on cache logic implemented elsewhere.
With just a few changes we’ve made cache management easier and more robust. Of course the code can be improved in many ways, but hopefully it gives some ideas on how to go about!