How do I clear a System.Runtime.Caching.MemoryCache

50,871

Solution 1

You should not call dispose on the Default member of the MemoryCache if you want to be able to use it anymore:

The state of the cache is set to indicate that the cache is disposed. Any attempt to call public caching methods that change the state of the cache, such as methods that add, remove, or retrieve cache entries, might cause unexpected behavior. For example, if you call the Set method after the cache is disposed, a no-op error occurs. If you attempt to retrieve items from the cache, the Get method will always return Nothing. http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx

About the Trim, it's supposed to work:

The Trim property first removes entries that have exceeded either an absolute or sliding expiration. Any callbacks that are registered for items that are removed will be passed a removed reason of Expired.

If removing expired entries is insufficient to reach the specified trim percentage, additional entries will be removed from the cache based on a least-recently used (LRU) algorithm until the requested trim percentage is reached.

But two other users reported it doesnt work on same page so I guess you are stuck with Remove() http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx

Update However I see no mention of it being singleton or otherwise unsafe to have multiple instances so you should be able to overwrite your reference.

But if you need to free the memory from the Default instance you will have to clear it manually or destroy it permanently via dispose (rendering it unusable).

Based on your question you could make your own singleton-imposing class returning a Memorycache you may internally dispose at will.. Being the nature of a cache :-)

Solution 2

I was struggling with this at first. MemoryCache.Default.Trim(100) does not work (as discussed). Trim is a best attempt, so if there are 100 items in the cache, and you call Trim(100) it will remove the ones least used.

Trim returns the count of items removed, and most people expect that to remove all items.

This code removes all items from MemoryCache for me in my xUnit tests with MemoryCache.Default. MemoryCache.Default is the default Region.

foreach (var element in MemoryCache.Default)
{
    MemoryCache.Default.Remove(element.Key);
}

Solution 3

Here's is what I had made for something I was working on...

public void Flush()
{
    List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
    foreach (string cacheKey in cacheKeys)
    {
        MemoryCache.Default.Remove(cacheKey);
    }
}

Solution 4

I know this is an old question but the best option I've come across is to

Dispose the existing MemoryCache and create a new MemoryCache object. https://stackoverflow.com/a/4183319/880642

The answer doesn't really provide the code to do this in a thread safe way. But this can be achieved using Interlocked.Exchange

var oldCache = Interlocked.Exchange(ref _existingCache, new MemoryCache("newCacheName"));
oldCache.Dispose();

This will swap the existing cache with a new one and allow you to safely call Dispose on the original cache. This avoids needing to enumerate the items in the cache and race conditions caused by disposing a cache while it is in use.


Edit

Here's how I use it in practice accounting for DI

public class CustomCacheProvider : ICustomCacheProvider
{
    private IMemoryCache _internalCache;
    private readonly ICacheFactory _cacheFactory;

    public CustomCacheProvider (ICacheFactory cacheFactory)
    {
        _cacheFactory = cacheFactory;
        _internalCache = _cacheFactory.CreateInstance();
    }
    public void Set(string key, object item, MemoryCacheEntryOptions policy)
    {
        _internalCache.Set(key, item, policy);
    }

    public object Get(string key)
    {
        return _internalCache.Get(key);
    }
   // other methods ignored for breviy 

    public void Dispose()
    {
        _internalCache?.Dispose();
    }

    public void EmptyCache()
    {
        var oldCache = Interlocked.Exchange(ref _internalCache, _cacheFactory.CreateInstance());
        oldCache.Dispose();
    }
}

The key is controlling access to the internal cache using another singleton which has the ability to create new cache instances using a factory (or manually if you prefer).

Solution 5

The details in @stefan's answer detail the principle; here's how I'd do it.

One should synchronise access to the cache whilst recreating it, to avoid the race condition of client code accessing the cache after it is disposed, but before it is recreated.

To avoid this synchronisation, do this in your adapter class (which wraps the MemoryCache):

public void clearCache() {
  var oldCache = TheCache;
  TheCache = new MemoryCache("NewCacheName", ...);
  oldCache.Dispose();
  GC.Collect();
}

This way, TheCache is always in a non-disposed state, and no synchronisation is needed.

Share:
50,871

Related videos on Youtube

Peter Marks
Author by

Peter Marks

Updated on May 04, 2022

Comments

  • Peter Marks
    Peter Marks about 2 years

    I use a System.Runtime.Caching.MemoryCache to hold items which never expire. However, at times I need the ability to clear the entire cache. How do I do that?

    I asked a similar question here concerning whether I could enumerate the cache, but that is a bad idea as it needs to be synchronised during enumeration.

    I've tried using .Trim(100) but that doesn't work at all.

    I've tried getting a list of all the keys via Linq, but then I'm back where I started because evicting items one-by-one can easily lead to race conditions.

    I thought to store all the keys, and then issue a .Remove(key) for each one, but there is an implied race condition there too, so I'd need to lock access to the list of keys, and things get messy again.

    I then thought that I should be able to call .Dispose() on the entire cache, but I'm not sure if this is the best approach, due to the way it's implemented.

    Using ChangeMonitors is not an option for my design, and is unnecassarily complex for such a trivial requirement.

    So, how do I completely clear the cache?

    • Marc Gravell
      Marc Gravell over 12 years
      MSDN explicitly states that it is not a singleton: "Another difference is that you can create multiple instances of the MemoryCache class for use in the same application and in the same AppDomain instance."
    • Peter Marks
      Peter Marks over 12 years
      @MarcGravell Ah! I was probably looking at the wrong documentation. So that would seem to imply that I can call .Dispose(), and then when I call MemoryCache.Default, I'll get a brand-spanking new instance...
    • Marc Gravell
      Marc Gravell over 12 years
      is simply swapping to a new instance and disposing the old a problem? assuming synced, etc
    • Marc Gravell
      Marc Gravell over 12 years
      no if you call dispose on the default instance, you've killed the default instance. So... don't use .Default if you are planning on using Dispose! That doesn't make it a singleton, though...
    • Peter Marks
      Peter Marks over 12 years
      There is a similar question which I only found now. But the discussion surrounding Dispose() is incomplete.
  • Peter Marks
    Peter Marks over 12 years
    So I could wrap an instance that I create--i.e. not the Default instance--in an adapter class, which is a singleton. Then when I want to clear the contained cache, I just create a new one, and whip the GC.
  • stefan
    stefan over 12 years
    @Peter Marks I see no reason why it would fail on you. And Dispose() should really do the trick in general but if its alot of data calling GC.Collect() may be a good idea.
  • BozoJoe
    BozoJoe almost 12 years
    really? a full blown GC.Collect?
  • RobSiklos
    RobSiklos about 10 years
    This solution definitely DOES NOT avoid the stated race condition. Consider thread A gets a reference to TheCache and starts doing operations on it. Thread B calls clearCache, which runs to completion. Thread A is still running, but its using the disposed cache.
  • Nick Jones
    Nick Jones almost 10 years
    To be clear, Trim takes a percentage, not an absolute count, so Trim(100) is indicating that 100% of the items -- not 100 items -- should be removed.
  • Steve Guidi
    Steve Guidi over 9 years
    Unfortunately, Trim(100) does not always evict 100% of the items in the cache: connect.microsoft.com/visualstudio/feedback/details/831755/…
  • Dave Black
    Dave Black over 9 years
    You should definitely add thread-synchronization here. Surprised you're not getting the "modifying this collection while enumerating" exception.
  • HMR
    HMR over 8 years
    I had no luck with Trim either so here is an example how you could use remove: stackoverflow.com/a/7334092/1641941
  • alastairtree
    alastairtree over 7 years
    MemoryCache implements IDisposable so you should call Dispose before replacing the old instance.
  • Admin
    Admin over 7 years
    Clean build process will clear the cache for local iis or iis express? And for hosted sites that are not on local iis what is the life time of cache? Is it forever until app pool recycled?
  • Nick
    Nick almost 7 years
    This usage shouldn't raise an IllegalOperationException due to how MemoryCache implements GetEnumerator(). It creates a new Dictionary<string, object> by copying data from it's internal storage and returns an enumerator for that new object, so the MemoryCache itself is not being enumerated and modified at the same time.
  • ChW
    ChW over 5 years
    I use this approach in my TestInitialize for unit tests. I recommend to avoid this in production code!
  • Frédéric
    Frédéric over 4 years
    The exchange is not enough IMO: how are concurrent readers protected from the dispose? I think you need a "multiple reads single write" lock to be really safe.
  • Alex from Jitbit
    Alex from Jitbit over 3 years
    MS docs state this: "Retrieving an enumerator for a MemoryCache instance is a resource-intensive and blocking operation. Therefore, the enumerator should not be used in production applications."
  • Gregory Ledray
    Gregory Ledray about 2 years
    If I inject the cache with DI as a singleton I don't understand how to run this code. Disposing of the MemoryCache works but assigning the new value in is hard since _existingCache must be a ref, which means it can't be a property, which means it can't be the private property I assigned the injected MemoryCache to when I injected it. Is there a way around this which lets me modify the object which is injected into the class? (I know this is a DI question; I just don't know how to solve it and if there's a better general question here)
  • Rondel
    Rondel about 2 years
    @GregoryLedray Updated the answer with some sample code that uses DI to provide a potential way to do this.