Redis as a Distributed Cache on .NET 6.0

Nishān Wickramarathna
8 min readApr 16, 2022

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. Well-known as a “data structure server”, with support for strings, hashes, lists, sets, sorted sets, streams, and more. Not only those, but also properties like Programmability, Extensibility, Persistence, Clustering, High availability makes Redis the most popular caching data store. Although today we’re talking about caching with Redis, It is can also be used as a real-time data store and also used for streaming & messaging. Redis is a way of implementing distributed caching for a web application. We talked about In Memory Caching last time, where I promised I’ll talk about distributed caching as well.

Without further a due we’ll start. But before that you need to have few prerequisites. (And don’t worry if you don’t know how to work with docker, we will not go deep into it, however I highly recommend you to learn it and use it for your development work because it is a very useful tool.)

☑ Docker Desktop installed

☐ .NET SDK 6.0 installed

☐ VS Code installed

Before we start, I highly recommend you to read my post about In Memory Caching, because it will introduce you to basic concepts of caching and some technical terms.

Source code for this entire project can be found here:

🏠Creating a Local Redis Server Using Docker

After installing Docker open up a terminal and execute,

docker run --name redis-local -p 5002:6379 -d redis
Output should look like this.

redis-local is the name that I’m giving to my local Redis instance. You can type any name you like here. Next the -p is the port mapping, it will map localhost:5002 to the redis docker container’s localhost:6379 (default Redis port number). -d runs container in background and print container ID.

Note: For the ease of accessing Redis from other containers via Docker networking, the “Protected mode” is turned off by default. This means that if you expose the port outside of your host (e.g., via -p on docker run), it will be open without a password to anyone. It is highly recommended to set a password (by supplying a config file) if you plan on exposing your Redis instance to the internet. Read more.

Now if you open up docker container list you will see it running. (Also you can use docker container ls command to see container list).

Notice that `redis-local` is running on port 5002
Output of `docker ps -a` (list all containers)

⚠ Note: for the moment we’ll do it on a local Redis server, I’ll show you later how to use Redis with Microsoft Azure. You can have it somewhere else as well. All you need is a connection string to that.

Now let’s login to the redis-cli running inside of the container. First command is docker exec -it redis-local sh, which will present us the shell on the container, from the shell we can login to the Redis database using redis-cli command.

docker exec -it redis-local sh -> redis-cli

Now if you execute dbsize command you will get an output like (integer) 0, meaning that there’s no data.

⚙️ Application Configuration for Redis

For this, I have created a demo ASP.NET MVC project from dotnet-cli using dotnet new mvc

First we need to install 2 Nuget packages. StackExchange.Redis and Microsoft.Extensions.Caching.StackExchangeRedis. [Check .csproj]

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package StackExchange.Redis

Next in the Program.cs add caching information to the dependency container, add the connection string to the appsettings.json

Notice the options.InstanceName, it’s going to be your application name and it’s gonna prepend any tag. Remember that Redis is a key value pair, your keys must be unique so if you are are shoving stuff into the database from multiple applications the odds that you use the same key in more than one application goes up. To reduce the chance of having two identical keys from two application, you must add an Instance name prefix so that each key is different. Connection string is localhost:5002, because we said docker to map Redis port 6379 to local port 5002.

Now we need 2 helper methods to make our lives a bit easier, because with this we can configure few options and provides an abstraction layer to the Redis server.

This is an extension method, that’s why we have this keyword in front of IDistributedCache

Here we have two methods, SetRecordAsync and GetRecordAsync. GetRecordAsync is simple, we pass the IDistributedCache and the record Id, then we lookup for the said recordId, using GetStringAsync(IDistributedCache, String, CancellationToken)

If no record is found for the given key, we are returning the default of T, which means we return the default value of the expected type. If we’re expecting an integer value, default will be 0, if we’re expecting a List<Model>, default will be null. If a record is available in the cache then deserialize the record and return.

Let’s look at the SetRecordAsync as well, there we also pass the IDistributedCache and the recordId(new record will be set using this Id), Next the data of type T (custom type/ generic type, because the items that we want to store in the cache are of different types, there are not always belong to the same type). Next absoluteExpireTime and slidingExpireTime, which I describe in the In Memory Caching on .NET 6 article. Please read the section “Expiration”. In here, notice that our cache expires in 60 seconds.

Inside the method, we set the 2 expiration times using DistributedCacheEntryOptions(), then serialize the data into JSON format, set the cache using SetStringAsync(). You can copy and paste this class to your application, because we have used generic types.

🚀Caching in Action

To simulate the database I have created a UserRepository, which will return a list of Users.

To use distributed caching you need to inject IDistributedCache to the Controller.

Now all you have to do is call those 2 caching helper methods in your Controllers or Razor pages, adhering to the following flow when data is requested.

Notice that recordKey here is the recordId for the CacheHelper class that we’re using to distinguish records, which includes current date and time.

I have added some extra implementation to the Index() method so that changes are clearly visible in the View, such as where we got the data from (from cache or database) and some styling. See my completed implementation here.

Now when you run the application for first time, there won’t be any data items in the cache, so data will be loaded from the database. For the next reload data will we available in the cache and will be served from there.

Now if you open redis-cli inside the docker container and run scan 0 (iterates the set of keys in the currently selected Redis database)(see more), you will see the stored keys in the format that we specified. e.g: “RedisDemo_Users_20220416_0849”

Output of `scan 0`

Use hgetall keyname to see list of fields and their values stored in the hash.

Output of `hgetall`

Note the this key has sldexp(sliding expiration), absexp (absolute expiration) and data fields in it. Download the demo project and play around with it.

🔌 About Azure Cache for Redis

Azure Cache for Redis can be used as a distributed data or content cache, a session store, a message broker, and more. It can be deployed as a standalone. Or, it can be deployed along with other Azure database services, such as Azure SQL or Cosmos DB.

Azure Cache for Redis improves application performance by supporting common application architecture patterns. Some of the most common include the following patterns:

[Source]

If you want to move your Redis server to the cloud, create a Redis database on Microsoft Azure, you can follow Microsoft guide to create a Redis cache.

After you create a cache, all you need is the connection string. It will look something like this:

redis-server-name.redis.cache.windows.net:6380,password=auto-generated-pwd,ssl=True,abortConnect=False

redis-server-name will be what you have specified while creating the Redis cache. Add this to the appsettings.json, just like how we did for the local Redis server.

Note: since Azure Redis instance has a password, don’t keep this in the source control. Using secrets.json in advised.

You can see that many requests (simulated using multiple browsers), now use the same cache and since now your cache server is standalone, you don’t have to worry about the scalability of your application, web server is not impacted by the cache growth.

I’m gonna wrap things up there. Hope you learned something new. Happy coding! 👋

Follow me | twitter.com/nishan_cw

--

--