From IDistributedCache to IMemoryCache

For a small site like mine, caching items locally in the web server's RAM is good enough. I initially used the IDistributedCache interface because it provides an implementation called Distributed Memory Cache that does exactly this. Despite its name, this implementation is not an actual distributed cache, it caches objects in memory. It's very simple to setup, it only needs the following line of code in the Startup.ConfigureServices


By using IDistributedCache I was also trying to be future proof. In case if one day my site gets high traffic, the IDistributedCache has other true distributed implementations I can use, such as the Distributed Redis Cache

However, IDistributedCache has its downside. Because it's designed to work with distributed caching solutions like Redis, its API is more complex. Check out its methods and extension methods, the set and get methods work with either strings or byte arrays not object types. This means objects in and out of IDistributedCache require serialization and de-serialization, and this step could be tricky and error prone.

ASP.NET Core does have another interface for in-memory caching called IMemoryCache. It's much easier to use and can store any object directly. This is a better fit for me, the overhead and complexity from IDistributedCache don't bring me an needed benefits for right now.

I redesigned caching by creating a new ICacheProvider interface to hide the actual implementation behind it, as shown in the following diagram. The interface has only two methods, one for adding or getting items to or from the cache, the other is for removing items from the cache. 

public interface ICacheProvider
    Task<T> GetOrAddAsync<T>(string key, 
	    TimeSpan cacheTime, 
	    Func<Task<T>> acquire) where T : class;

    void Remove(string key);

I have three implementations but only the first one MemCache is actually used and it's implemented using IMemoryCache. The second one NoCache, as the name suggests, is not caching anything, it's used for debugging purpose. The last DistributedCache is not currently implemented and is here for future reference.


User chooses which cache implementation to use in appsettings.json

// "Memory": default, uses server memory
// "Distributed": not implemented
// "NoCache": for debugging, it turns off cache
"CacheProviderType": "Memory",

Really, right now "Memory" is the one to use. But, the "NoCache" option is quite useful, because caching often interferes with debugging. Notice GetOrAddAsync method on the interface has a delegate parameter. The first time it's called, it will result in items retrieved and stored into the cache, subsequent calls will not run the delegate anymore. Setting it to "NoCache" will stop caching anything, thus making debugging into the delegate all the time possible.