In-memory & Distributed (Redis) Caching in ASP.NET Core

Sena Kılıçarslan
.NET Core
Published in
6 min readJan 26, 2020

In this post, I will demonstrate how to use in-memory caching and Redis based distributed caching in an ASP.NET Core Web API.

I will use the following tools & technologies:

  • ASP.NET Core 3.1
  • Visual Studio 2019
  • Redis

Let’s start with the definition of caching.

Caching

A cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.[1]

Image source

Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. Caching works best with data that changes infrequently and is expensive to generate. Caching makes a copy of data that can be returned much faster than from the source. Apps should be written and tested to never depend on cached data.[2]

Now, I will present the API in which we will use caching.

About the API

The API returns the movies of an actor/actress given as a parameter and looks like below:

It makes an external API call to the TMDb API and the controller code is shown below:

As you see, an external API call is made for every query. However, there will be cases like the same actor/actress is queried several times by the same or different users. So, we can increase the performance of this application by using caching mechanism instead of calling the external API again and again for the same actors/actresses.

First, let’s see how we can implement in-memory caching in this API.

In-memory Caching

ASP.NET Core supports several different caches. The simplest cache is based on the IMemoryCache. IMemoryCache represents a cache stored in the memory of the web server. Apps running on a server farm (multiple servers) should ensure sessions are sticky when using the in-memory cache. Sticky sessions ensure that subsequent requests from a client all go to the same server.[3]

In-memory caching is a service that’s referenced from an app using Dependency Injection. So, we first need to register this service to the built-in IoC container of ASP.NET Core by modifying ConfigureServices method of Startup.cs as below:

Then we inject IMemoryCache to the constructor:

And we change GetMovieList method as below:

As you see in the above code, we first check if the movie list exists in the cache and return from there if it does. Otherwise, we call the external API and cache the result.

Besides, we set the cache expiration options which are explained below:

SlidingExpiration: Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration (if set).

AbsoluteExpiration: Gets or sets an absolute expiration date for the cache entry.

A cached item set with a sliding expiration only is at risk of becoming stale. If it’s accessed more frequently than the sliding expiration interval, the item will never expire. Combine a sliding expiration with an absolute expiration to guarantee that the item expires once its absolute expiration time passes.[4]

In the next section, we will implement distributed caching for the same controller.

Distributed Caching

A distributed cache is a cache shared by multiple app servers, typically maintained as an external service to the app servers that access it. A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.

A distributed cache has several advantages over other caching scenarios where cached data is stored on individual app servers. When cached data is distributed, the data:

  • Is coherent (consistent) across requests to multiple servers.
  • Survives server restarts and app deployments.
  • Doesn’t use local memory.[5]
Image source

There are two main disadvantages of the shared caching approach:

  • The cache is slower to access because it is no longer held locally to each application instance.
  • The requirement to implement a separate cache service might add complexity to the solution.[6]

We will use Redis-based distributed caching in our API. So let’s continue with what Redis is and how to install it.

Redis

The name Redis means REmote DIctionary Server.

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.[7]

Now, I will show how to install Redis in a Windows machine using Chocolatey. (If you don’t have Chocolatey you can install it from here.)

First, install the Chocolatey Redis package.

Then run redis-server from a command prompt.

Now, the Redis server is up and running. We can test it using the redis-cli command.

Open a new command prompt and run redis-cli on it and try the following commands:

Now that we installed the Redis-server and saw that it is working properly, we can modify the API to use Redis-based distributed caching.

Modify the API

ASP.NET Core provides IDistributedCache interface to interact with the distributed caches including Redis.

  • Get , GetAsync : Accepts a string key and retrieves a cached item as a byte[] array if found in the cache.
  • Set , SetAsync : Adds an item (as byte[] array) to the cache using a string key.
  • Refresh , RefreshAsync : Refreshes an item in the cache based on its key, resetting its sliding expiration timeout (if any).
  • Remove , RemoveAsync : Removes a cache item based on its string key.

To use a Redis distributed cache, we need to add a package reference to Microsoft.Extensions.Caching.StackExchangeRedis package in our project.

Then we add the following line to ConfigureServices method of the Startup.cs for configuration the cache implementation using a RedisCache instance:

Our approach in modifying the controller will be very similar to what we did in the in-memory caching section.

First, we inject IDistributedCache to the controller:

Then we change the GetMovieList method as follows:

As mentioned above, GetAsync and SetASync methods work with byte[] arrays, so I encoded the serialized movies list to byte[] array. Also, I want to mention that there are extension methods where you do not need to convert the value to byte[] array and these are GetStringAsync and SetStringAsync.

You can find the source code in this GitHub repository.

That’s the end of the post. I hope you found this post helpful and easy to follow. If you have any questions and/or comments, you can share them in the responses section below.

And if you liked this post, please clap your hands 👏👏👏

Bye!

--

--

Sena Kılıçarslan
.NET Core

A software developer who loves learning new things and sharing these..