C# - Great and simple implementation of MemoryCache - updated 2020

Years ago I made this blog post on a memorycache implementation done by Falafel software. I had a task where I needed to use caching so I decided to look for great implementations of MemoryCache for inspiration. I found the one from Falafel and decided to use it.

What I wanted was a simple implementation basically it should just have two functions: Caching objects and getting objects - or a combination of the two. In this implementation a Lazy is used to make the initialisation of what you put into the cache thread-safe, meaning whatever you put into the lazy, it will only be loaded once. This is good as what we are caching is often time consuming. Besides leveraging the Lazy class it also uses the built-in method GetOrAddExisting, which is a nice way to - yes, get or add an object. instead of having to care about updating and removing from cache: you get the object if it is there and if not it is added.

If you do not worry about calling time-consuming code more than once, you could potentially just use the AddOrGetExisting method from the memorycache class directly. Here is the implementation from Falafel:

public static class ExampleCache {
    private static MemoryCache _cache = new MemoryCache("ExampleCache");
        
    public static object GetItem(string key) {
        return AddOrGetExisting(key, () => InitItem(key));
    }
    
    private static T AddOrGetExisting<T>(string key, Func<T> valueFactory)
    {
        var newValue = new Lazy<T>(valueFactory);
        var oldValue = _cache.AddOrGetExisting(key, newValue, new CacheItemPolicy()) as Lazy<T>;
        try
        {
            return (oldValue ?? newValue).Value;
        }
        catch
        {
            // Handle cached lazy exception by evicting from cache. Thanks to Denis Borovnev for pointing this out!
            _cache.Remove(key);
            throw;
        }       
    }
    
    private static object InitItem(string key) {
        // Do something expensive to initialize item
        return new { Value = key.ToUpper() };
    }
}

The only downside I found using this implementation is that if you are logging the entries added to or removed from your cache, you logs will read that your entry gets added but the catch part removes it again. This may seem confusing in your logs, as items seem to be added only to be removed soon after. This is due to the deferred nature of Lazy, which is also the strength of this implementation

The solution does not set up a specific CacheItemPolicy. It merely uses the default values. This is up to you to configure - you can get some pointers on how to do that here.

I hope this helps you, as it helped me. Head over to falafel's blog to read an in depth description.