Using InMemory Cache In .Net Core

In a previous post, we talked about how to use a Redis Cache in .net Core. In most large scale scenarios, Redis is going to be your goto. But for tiny sites that have a single web instance, or for sites that really only need a local cache, InMemory caching is much easier to get setup with and obviously does away with wrangling a Redis server.

Interestingly, .net Core currently offers two ways to implement a local in memory cache. We’ll take a look at both.

IMemoryCache

The first option is to use what is simply known in .net core as IMemoryCache. It’s similar to what you may have used in standard ASP.net in terms of storing an object in memory by a key.

First open up your startup.cs. In your ConfigureServices method you need to add a call to “AddMemoryCache” like so :

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvc();
	services.AddMemoryCache();
}

In your controller or class you wish to use the memory cache, add in a dependency into the constructor. The two main methods you will likely be interested in are “TryGetValue” and “Set”. Both should be rather explanatory in the following code :

public class HomeController : Controller
{
	private readonly IMemoryCache _memoryCache;

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

	[HttpGet]
	public string Get()
	{
		var cacheKey = "TheTime";
		DateTime existingTime;
		if (_memoryCache.TryGetValue(cacheKey, out existingTime))
		{
			return "Fetched from cache : " + existingTime.ToString();
		}
		else
		{
			existingTime = DateTime.UtcNow;
			_memoryCache.Set(cacheKey, existingTime);
			return "Added to cache : " + existingTime;
		}
	}
}

That’s the basics, now a couple of nice things that this implementation of a memory cache has that won’t be available in the next implementation I will show you

PostEvictionCallback
An interesting feature is the PostEvictionCallback delegate. This allows you to register an action to be called everytime something “expires”. To use it, it will look something like the following :

_memoryCache.Set(cacheKey, existingTime, 
	new MemoryCacheEntryOptions()
		.RegisterPostEvictionCallback((key, value, reason, state) => {  /*Do Something Here */ })
 );

Now everytime a cache entry expires, you will be notified about it. Usually there are very limited reasons why you would want to do this, but the option is there should you want it!

CancellationToken
Also supported is CancellationTokens. Again, a feature that probably won’t be used too often, but can be used to invalidate a set of cache all in one go. CancellationTokens are notoriously difficult to debug and get going, but if you have the need it’s there!

The code would look something similar to this :

var cts = new CancellationTokenSource();
_memoryCache.Set(cacheKey, existingTime, 
	new MemoryCacheEntryOptions()
		.AddExpirationToken(new CancellationChangeToken(cts.Token))
 );

Distributed Memory Cache

A “distributed” memory cache is probably a bit of an oxymoron. It’s obviously not distributed if it’s sitting local to a machine. But the big advantage to going down this road is that should to intend to switch to using Redis in the future, the interfaces between the RedisDistributedCache and the In Memory one are exactly the same. It’s just a single line of difference in your startup. This may also be helpful if locally you just want to use your machines cache and not have Redis setup.

To get going, in your startup.cs ConfigureServices method, add a call to AddDistributedMemoryCache like so :

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvc();
	services.AddDistributedMemoryCache();
}

Your controller or class where you inject it should look a bit like the following. Again, this code is actually taken directly from our Redis Cache tutorial, the implementation is exactly the same for InMemory, it’s only the call in ConfigureServices in your startup.cs that changes.

public class DistributedController : Controller
{
	private readonly IDistributedCache _distributedCache;

	public DistributedController(IDistributedCache distributedCache)
	{
		_distributedCache = distributedCache;
	}

	[HttpGet]
	public async Task<string> Get()
	{
		var cacheKey = "TheTime";
		var existingTime = _distributedCache.GetString(cacheKey);
		if (!string.IsNullOrEmpty(existingTime))
		{
			return "Fetched from cache : " + existingTime;
		}
		else
		{
			existingTime = DateTime.UtcNow.ToString();
			_distributedCache.SetString(cacheKey, existingTime);
			return "Added to cache : " + existingTime;
		}
	}
}

Now, looking at what we said about IMemoryCache above, PostEvictionCallback and CancellationTokens cannot be used here. This makes sense because this interface for the most part is supposed to be used with distributed environments, any machine in the environment (Or the cache itself) could expire/remove a cache entry.

Another very important difference is that while IMemoryCache accepts C# “objects” into the cache, a distributed cache does not. A distributed cache can only accept byte arrays or strings. For the most part this isn’t going to be a big deal. If you are trying to store large objects just run them through a JSON serializer first and store them as a string, when you pull them out deserialize them into your object.

Which Should You Pick?

You are likely going to write an abstraction layer on top of either caching interface meaning that your controllers/services aren’t going to see much of it. For what you should use in that abstraction, I tend to go with the Distributed Cache for no other reason that should I ever want to move to using Redis, I have the option there.

2 thoughts on “Using InMemory Cache In .Net Core”

  1. Thanks for the article
    However I’m a little confused regarding when and how to use Redis.
    I would have liked to use Redis to preserve a shopping cart, say for example if a users shopping cart in stored in redis, and that server which the user is on crasches then another server can take over and the shopping cart will remain intact, with the user not noticing anything. Is this possible using redis
    Second how would I store a how shopping cart object into redis

    Reply
    • This would be a valid usecase for Redis (Although possibly not “cache” persay). It would depend on the Redis library you are using, or if you use distributed cache, you would need to serialize your object into a string (using JSON.net). There is a guide for how to use Redis as a distributed cache here

      Reply

Leave a Comment