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
Let’s start with the definition of 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.
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.
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.
ASP.NET Core supports several different caches. The simplest cache is based on the IMemoryCache.
IMemoryCacherepresents 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.
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.
In the next section, we will implement distributed caching for the same controller.
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.
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.
We will use Redis-based distributed caching in our API. So let’s continue with what Redis is and how to install it.
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.
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.
redis-server from a command prompt.
Now, the Redis server is up and running. We can test it using the
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.
GetAsync: Accepts a string key and retrieves a cached item as a
bytearray if found in the cache.
SetAsync: Adds an item (as
bytearray) to the cache using a string key.
RefreshAsync: Refreshes an item in the cache based on its key, resetting its sliding expiration timeout (if any).
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,
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
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 👏👏👏