8

How to correctly clear IMemoryCache from ASP.NET Core?

I believe this class is missing Clear method, but anyway how to deal with it? In my project I'm caching DocumentRepository's methods for 24 hours where I'm getting lots of rows from database. But sometimes I can change the database, so I want to clear the IMemoryCache as it has got rubbish data.

CSharpBeginner
  • 1,625
  • 5
  • 22
  • 36
  • Check the solution posted here: https://stackoverflow.com/questions/4183270/how-to-clear-memorycache/22388943#22388943 – Mohsin Mehmood May 26 '19 at 18:15
  • @MohsinMehmood: Question is tagged ASP.NET Core. .NET Core's implementation of `IMemoryCache` is pretty simple, with just 3 methods: `TryGetValue`, `CreateEntry` and `Remove`. The [implementation itself](https://github.com/aspnet/Extensions/blob/master/src/Caching/Memory/src/MemoryCache.cs) doesn't have any enumerable method, so there is no way to enumerate it. Linked question is about .NET Framework – Tseng May 26 '19 at 18:19
  • Does this answer your question? [How to remove all objects (reset ) from IMemoryCache in ASP.NET core](https://stackoverflow.com/questions/34406737/how-to-remove-all-objects-reset-from-imemorycache-in-asp-net-core) – janw Feb 19 '22 at 19:37

4 Answers4

9

IMemoryCache indeed lacks a Clear() method, but it's not hard to implement one yourself.

    public class CacheWrapper
    {
        private readonly IMemoryCache _memoryCache;
        private CancellationTokenSource _resetCacheToken = new();

        public CacheWrapper(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

        public void Add(object key, object value, MemoryCacheEntryOptions memoryCacheEntryOptions)
        {
            using var entry = _memoryCache.CreateEntry(key);
            entry.SetOptions(memoryCacheEntryOptions);
            entry.Value = value;

            // add an expiration token that allows us to clear the entire cache with a single method call
            entry.AddExpirationToken(new CancellationChangeToken(_resetCacheToken.Token));
        }

        public void Clear()
        {
            _resetCacheToken.Cancel(); // this triggers the CancellationChangeToken to expire every item from cache

            _resetCacheToken.Dispose(); // dispose the current cancellation token source and create a new one
            _resetCacheToken = new CancellationTokenSource();
        }
    }

Based on https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1#cache-dependencies and https://stackoverflow.com/a/45543023/2078975

Mr. T
  • 3,892
  • 3
  • 29
  • 48
  • For some unclear reason `CreateEntry` did not work for me in .NET 6 but `_memoryCache.Set(key, value, new CancellationChangeToken(_resetCacheToken.Token))` worked for me. – Kristofer Jun 07 '22 at 12:51
  • @Kristofer `CreateEntry()` is in [`Microsoft.Extensions.Caching.Memory.IMemoryCache`](https://github.com/dotnet/runtime/blob/bf7cdc5825f94cf67d69ab5f0ea0c2dc9490861b/src/libraries/Microsoft.Extensions.Caching.Abstractions/src/IMemoryCache.cs) (use the `Microsoft.Extensions.Caching.Memory` NuGet package) – Mr. T Jul 19 '22 at 18:13
5

There is an easy way to clear it that may not work in all environments, but where it does work, it works well.

If you're getting your IMemoryCache object through dependency injection, there is a good chance you'll actually be receiving a MemoryCache object. MemoryCache does have a method called Compact() that will clear the cache. You can try to cast the object to MemoryCache and, if successful, call Compact.

Example:

string message;
// _memoryCache is of type IMemoryCache and was set via dependency injection.
var    memoryCache = _memoryCache as MemoryCache;
if (memoryCache != null)
{
    memoryCache.Compact(1);
 
    message ="Cache cleared";
 } else {
    message = "Could not cast cache object to MemoryCache. Cache NOT cleared.";
 }
Mark Meuer
  • 7,200
  • 6
  • 43
  • 64
1

The cache class and interface don't have any methods for clearing neither ones to iterate over the keys, since it's not meant to be a list and in ASP.NET Core applications one usually use IDistributedCache interface as dependency, since it easier allows you to later change from a local memory cache to a distributed cache (such as memd or Redis).

Instead, if you want to invalidate a specific row, you should remove the cached entry via cache.Remove(myKey).

Of course, this requires you to know the key you want to invalidate. Typically you do that via events. Every time you update an entry in the database, you would fire up an event. This event will be caught by a background service and cause a cache invalidation.

Locally this can be done with any pub/sub library. In distributed scenarios you may want to use pub/sub features of the distributed cache (i.e. Redis).

In cases of lookup tables (where many values are affected), you can have a service refresh the cache (i.e. every 5 to 10 minutes via a background task using a scheduling library such as hangfire or quart.net).

Home work Questions

But one question you should ask yourself: Is it really a good idea to cache documents for 24 hours if they change frequently?

Does the loading of a single document takes so much time, that caching it 24 hours will be worth? Or are shorter times good enough (15, 30, 60 minutes)?

Community
  • 1
  • 1
Tseng
  • 61,549
  • 15
  • 193
  • 205
  • It' doesnt really change frequently. It can change even once per month or two, but ony day it can change, so I have to clear the cache, as it would not be up-to-date – CSharpBeginner May 26 '19 at 18:34
  • Well like I said, dunno how long time it takes for you to fetch the document on a cache miss (if its less than 10 ms and this document is read a couple of times within the cache time), its pretty acceptable to have cache time as low as 5, 10 or 15 minutes). If your documents take so very long to obtain I'd look in there. That's if the additional infrastructure ( [distributed] pub/sub) is out of scope for you – Tseng May 26 '19 at 22:48
-1

To clear you MemoryCache, just remove your memory cache by key, like this:

memoryCache.Remove("your_key");