Distributed Caching with Redis and Response Caching in ASP.NET Core 8

Charles
6 min readNov 29, 2023

--

Caching allows applications to store regularly accessed data in memory or a data store, enhancing application performance by retrieving the cached data instead of fetching it from the original source, such as the database.

Redis in asp.net core

In ASP.NET Core, there are different ways to perform caching which are:

  • Memory cache (IMemoryCache)
  • Response caching
  • Distributed Caching (IDistributedCache)

Memory Cache

The memory cache is one of the simplest caching in ASP.NET Core that provides an interface “IMemoryCache” that caches data in the server memory and it also works with sticky session that ensures that all request goes to a single server. For apps running on multiple servers, the IMemoryCache should not be used, instead use a distributed cache that supports non-sticky session. This article is not about memory caching so let’s move to the next.

Response Caching

Response caching (as the name implies) ensures that responses are cached, therefore reducing the client request that hits the server. Response caching works with HTTP headers and they are very useful. Imagine every time you send a request to an API endpoint, it takes about 5 mins to get a response from the server and the data that is returned is like a static data or data that doesn't change frequently. Response caching helps to cache the response by reducing the work the server has to perform to generate a response so that subsequent request would in turn be given a cached response.

The response cache can also vary based on some information like “User-Agent” and it also has a duration or max-age. To use the Response caching in ASP.NET Core, we have to specify the ResponseCache Attributes in the Action method and its Parameters:

  • Duration: The durations Gets or Sets the max-age in seconds in the Cache-Control header
  • Location: Specifies where the data from a url should be cached. The ResponseCacheLocation.Any ensures that it is cached from both client and proxy
  • VaryByHeader: The VaryByHeader is optional but it ensures that the response cache can vary based on a given header like “Auhorization”. The code below specify the VaryByHeader parameter to “Authorization” therefore the cache response would be varied by “Authorization”. For example, if “User A” sends a fresh request, the server would return a response that would be cached subsequently. If “User A” sends another request with a different authorization header, that response would not be cached until subsequent requests because the authorization header has changed.

[HttpGet]
[ResponseCache(Duration=60, Location=ResponseCacheLocation.Any, VaryByHeader="Authorization")]
public async Task<IActionResult> Countries()
{
var countries = await _countryRepo.GetAll(c=>c.IsActive);
return Ok(countries);
}

When the request is sent to the country endpoint, the response comes in cached. If we inspect using the developer console, the response header has some cache information such as: Cache-Control that set the max-age to 60 seconds and the Vary to “Authorization”. So all subsequent request would be cached until max-age expires.

Request and Response Header in Developer console

Distributed Caching with Redis

Distributed caching supports a non-sticky session, that is, it can run on multiple servers and can survive server restarts. A distributed cache can improve the performance of an ASP.NET Core application. The interface that supports the use of distributed caching is the “IDistributedCache” interface and supports third party providers such as: NCache and Redis.

In production environment Distributed Redis cache is the preferred approach as it reduces the loads on the database and speeds up the retrieval of data.

Installing Redis with Docker

To get started with Redis, we need to install Redis and get it running. To do this, we use docker to it on the OS by using this command in a the terminal:

docker pull redis

There are also several ways to install it, but docker seems to be the fastest way. Note, Docker must be installed and running for the command above to work properly. After pulling the image, we need a way to start up a container from the Redis image.

docker run -p 6379:6379 --name some-redis -d redis

The command above gets the Redis container up and running.

redis container

The IDistributedCache Interface

The IDistributedCache interface is used to store and retrieve cache data from the cache. It provides several methods that is used to cache data which are:

  • SetAsync: Adds an item to the cache using a specified string key
  • GetAsync: Retrieves an item from the cache using a specified string key
  • GetStringAsync: Retrieves the string values from a specified key

Others are RefreshAsync that refreshes the cache and RemoveAsync that removes a cache using the specified key.

Distributed Redis Cache implementation in ASP.NET Core

Now we have the Redis server running, let’s wire up the implementation in code. To get started, create an empty ASP.NET Core API project and add the controller services and also install the Microsoft.Extensions.Caching.StackExchangeRedis package by executing the command below or directly from the nuget package manager

dotnet add [<project>] package Microsoft.Extensions.Caching.StackExchangeRedis

After installing the package, we need to add it to the service collection in the Program.cs class

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddStackExchangeRedisCache(action=>{
var connection = "localhost:6379"; //the redis connection
action.Configuration = connection;
});

var app = builder.Build();
app.MapControllers();
app.Run();

Now we have added the Redis cache in the service collection, we need to see it in action. First, a country class is defined to hold country data as shown below:

public class Country
{
public int Id { get; set;}
public string Name { get; set; }

public List<Country> getData()
{
var country = new List<Country>
{
new()
{
Id = 1,
Name = "Nigeria"
},
new()
{
Id=2,
Name="Ghana"
},
new()
{
Id=3,
Name="Kenya"
}
};
return country;
}
}

The country class just contain few properties and a method that holds a list of countries. Our goal is to be able to cache the country data, so when a another request comes in, the data is being loaded from Redis cache. So, lets define a controller

using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;

namespace RedisTraining.Controllers
{
[ApiController]
[Route("[controller]")]
public class CountryController: ControllerBase
{
Country country;
IDistributedCache _distributedCache;
public CountryController(IDistributedCache distributedCache)
{
country = new Country();
_distributedCache = distributedCache;
}
[HttpGet("countries")]
public async Task<IActionResult> Countries()
{
string cachedCountry = await _distributedCache.GetStringAsync("country");
if(!string.IsNullOrEmpty(cachedCountry))
{
return Ok(new{cachedData = cachedCountry});
}
//add to redis cache
var countries = country.getData();
var serialize = JsonSerializer.Serialize(countries);
await _distributedCache.SetStringAsync("country", serialize);
return Ok(countries);
}
[HttpGet("cached/remove")]
public async Task<IActionResult> ClearCachedCountry()
{
await _distributedCache.RemoveAsync("country");
return Ok("Removed");
}
}
}

In the Countries() action method, we are returning countries that has been cached or not. If the country has been cached already, then the cached country is returned otherwise the countries are serialized and added to the cache. There is also another action method that removes the cached country by the specified key.

The IDistributedCache interface supports many cached providers, so regardless of which provider that is been used, this interface support them.

Conclusions

Caching brings good performance to ASP.NET Core applications. Storing frequently accessed data in a cache reduces the load on the database. Redis is fast and it has better performance compared to caching with SQL Server. If you also want to view and manage the data that was cached, you can download RedisInsight that provides a GUI for doing such.

RedisInsight is a visual tool that provides capabilities to design, develop and optimize your Redis application. Query, analyse and interact with your Redis data. Download it here!

Enroll for my course on the new ASP.NET Core 8 with the new features introduced on Udemy. This course covers the essentials of an ASP.NET Core 8 application and also clean architecture with a demo Task Management Application created from ground up with Entity Framework and Rich Domain Modelling. It also discusses about advanced security and good performance practices such as caching, rate limiting and connection resiliency of a web application.

I hope you enjoy the article

Cheers!

--

--