Step-by-Step Guide to Implementing Redis Caching in ASP.NET Core
Boost Performance and Scalability with In-Memory Caching Techniques
Caching is a key technique for boosting the performance and scalability of web applications. In this article, we’ll set up caching in an ASP.NET Core Web API using Redis, a powerful in-memory data store.
Prerequisites
ASP.NET Core Web API project: Ensure you have an existing ASP.NET Core Web API project.
Redis: For local development, you can use Docker to set up Redis.
docker run --name redis-cache -d -p 6379:6379 redis
Windows
Redis does not officially support Windows, but you can use the community-supported builds.
Download Redis for Windows:
Visit the GitHub page: https://github.com/tporadowski/redis.
Download the latest
.zip
file from the "Releases" section.
Install Redis:
Extract the downloaded
.zip
file.Navigate to the extracted folder and run
redis-server.exe
to start the Redis server.
Linux
Update Package Manager:
sudo apt update
Install Redis:
sudo apt install redis-server
Start Redis:
sudo systemctl start redis
Enable Redis to Start on Boot:
sudo systemctl enable redis
Test Redis Installation:
redis-cli ping
If Redis in running, you will see:
PONG
macOS
Install Using Homebrew:
brew install redis
Start Redis:
brew services start redis
Test Redis Installation:
redis-cli ping
After succession, you should see:
PONG
NuGet Packages: Install the following NuGet packages in your project:
StackExchange.Redis
Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package StackExchange.Redis
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
Step 1: Configure Redis in ASP.NET Core
In the Program.cs
file, configure Redis as the caching provider:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
// Configure Redis caching
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "SampleApp_";
});
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Add the Redis connection string to the appsettings.json
file:
{
"ConnectionStrings": {
"Redis": "localhost:6379"
}
}
Step 2: Create a Caching Service
Create a service to manage caching logic. Add a new class RedisCacheService
:
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
namespace CachingProject.Services
{
public class RedisCacheService
{
private readonly IDistributedCache _cache;
public RedisCacheService(IDistributedCache cache)
{
_cache = cache;
}
public async Task SetCacheAsync<T>(string key, T value, TimeSpan expirationTime)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expirationTime
};
var jsonData = JsonSerializer.Serialize(value);
await _cache.SetStringAsync(key, jsonData, options);
}
public async Task<T?> GetCacheAsync<T>(string key)
{
var jsonData = await _cache.GetStringAsync(key);
return jsonData is null ? default : JsonSerializer.Deserialize<T?>(jsonData);
}
public async Task RemoveCacheAsync(string key)
{
await _cache.RemoveAsync(key);
}
}
}
Step 3: Register the Caching Service
In Program.cs
, register the RedisCacheService
:
builder.Services.AddSingleton<RedisCacheService>();
Step 4: Use Redis Caching
You can use this service as per your architecture style here we are showing Redis Caching in a Controller, For example, in a EmployeesController
:
using System.Text.Json;
using CachingProject.Data;
using CachingProject.Dto;
using CachingProject.Model;
using CachingProject.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace CachingProject.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
private readonly AppDbContext _context;
private readonly RedisCacheService _cacheService;
public EmployeesController(AppDbContext context, RedisCacheService cacheService)
{
_context = context;
this._cacheService = cacheService;
}
// Add Employee API
[HttpPost]
public async Task<IActionResult> AddEmployee([FromBody] AddEmployeeDto addEmployeeDto)
{
if (addEmployeeDto == null)
{
return BadRequest("Employee data is required.");
}
var employee = new Employee
{
FirstName = addEmployeeDto.FirstName,
LastName = addEmployeeDto.LastName,
Email = addEmployeeDto.Email,
Role = addEmployeeDto.Role,
DateOfJoining = addEmployeeDto.DateOfJoining
};
_context.Employees.Add(employee);
await _context.SaveChangesAsync();
return Ok(new { Message = "Employee added successfully", EmployeeId = employee.Id });
}
// Get Employees API
[HttpGet]
public async Task<IActionResult> GetEmployees()
{
var cacheKey = "employees";
// Check if data exists in cache
var cachedProduct = await _cacheService.GetCacheAsync<List<EmployeeDto>>(cacheKey);
if (cachedProduct != null)
{
return Ok(new { Source = "cache", cachedProduct });
}
// if not in cache then fetch data from a database
var employees = await _context.Employees
.Select(e => new EmployeeDto
{
Id = e.Id,
FirstName = e.FirstName,
LastName = e.LastName,
Email = e.Email,
Role = e.Role,
DateOfJoining = e.DateOfJoining
})
.ToListAsync();
// Save data to cache for required timespan
await _cacheService.SetCacheAsync(cacheKey, employees, TimeSpan.FromMinutes(5));
return Ok(new { Source ="Database",employees});
}
}
}
Step 5: Testing the Caching Implementation
Run the application and access
GetEmployees
endpoint, use break point for better understanding and go line by line.The first time, data will be retrieved from the database and then it will be saved to the cache.
Subsequent requests will retrieve data from the Redis cache for the specified time duration.
Step 6: Verify Implementation
To verify, run the application and see the response from endpoint:
Since this is the first request, the data will be retrieved from the database.
Now, let's try again. This time, the data should be fetched from the cache, so fingers crossed.🤞.
You can use Redis CLI to verify the data stored in Redis:
If only string values are stored in Redis, you can use
GET SampleApp_employees
. Here,SampleApp_
is the instance name we set up inProgram.cs
, and "employees" is the key we used when setting or retrieving cached data. In the controller, we defined it asvar cacheKey = "employees";
.If you see the error
(error) WRONGTYPE Operation against a key holding the wrong kind of value
, it means you are trying to retrieve a key as a string (GET
), but the key holds a different type of value, like a list, hash, or set. In our case, the keySampleApp_employees
does not hold a string but a complex data type because we store a list of objects under this key.Steps to Debug and Fix:
Check the Key Type: Use the
TYPE
command in Redis CLI to determine the data type of the key:TYPE SampleApp_employees
Possible results:
string
: The key holds a string value.list
,hash
,set
, etc.: The key holds a different type of value.
Note: in our case key will be of hash type.
Inspect the Key Value: If the key is a hash, list, or another type, we need to use the appropriate command to view its contents. For example:
For a hash:
HGETALL SampleApp_employees
For a list:
LRANGE SampleApp_employees 0 -1
Since our key holds a hash type value, we will use:
HGETALL SampleApp_employees
. This should display the cached data in JSON format.
Conclusion:
By integrating Redis caching into an ASP.NET Core Web API, we can greatly enhance the performance and scalability of our application. Redis's speed and flexibility make it an excellent choice for caching solutions in modern web applications.