Redis Caching with IDistributedCache in C
This article provides a practical guide to implementing distributed
caching in .NET applications using the built-in
IDistributedCache interface backed by Redis. You will learn
which NuGet packages to install, how to register Redis within the
dependency injection container, and how to write efficient, reusable
methods for storing, retrieving, and serializing cached data using the
cache-aside pattern.
1. Install the Required NuGet Package
To enable Redis support for IDistributedCache, install
the Microsoft.Extensions.Caching.StackExchangeRedis package
via the .NET CLI or the NuGet Package Manager:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis2. Configure Redis in Application Startup
Open Program.cs and register the Redis cache service
with the dependency injection container using
AddStackExchangeRedisCache.
var builder = WebApplication.CreateBuilder(args);
// Register Redis Distributed Cache
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis") ?? "localhost:6379";
options.InstanceName = "SampleApp_"; // Prefix for all keys stored in Redis
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();In appsettings.json, add the connection string:
{
"ConnectionStrings": {
"Redis": "localhost:6379,abortConnect=false"
}
}3. Create Extension Methods for Serialization
IDistributedCache natively works only with
byte[] arrays or strings. To handle complex C# objects
seamlessly, create helper extension methods using
System.Text.Json:
using System.Text.Json;
using Microsoft.Extensions.Caching.Distributed;
public static class DistributedCacheExtensions
{
public static async Task SetRecordAsync<T>(
this IDistributedCache cache,
string recordId,
T data,
TimeSpan? absoluteExpireTime = null,
TimeSpan? slidingExpireTime = null)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = absoluteExpireTime ?? TimeSpan.FromMinutes(60),
SlidingExpiration = slidingExpireTime
};
var jsonData = JsonSerializer.Serialize(data);
await cache.SetStringAsync(recordId, jsonData, options);
}
public static async Task<T?> GetRecordAsync<T>(
this IDistributedCache cache,
string recordId)
{
var jsonData = await cache.GetStringAsync(recordId);
if (string.IsNullOrEmpty(jsonData))
{
return default;
}
return JsonSerializer.Deserialize<T>(jsonData);
}
}4. Implement the Cache-Aside Pattern
Inject IDistributedCache into your services or
controllers. When requesting data, check the cache first. If the item is
absent (a cache miss), retrieve it from the database, save it into
Redis, and return it.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IDistributedCache _cache;
public ProductsController(IDistributedCache cache)
{
_cache = cache;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
string cacheKey = $"Product_{id}";
// Attempt to fetch from Redis
var product = await _cache.GetRecordAsync<Product>(cacheKey);
if (product is null)
{
// Cache miss: retrieve from database
product = await FetchProductFromDatabaseAsync(id);
if (product is null)
{
return NotFound();
}
// Save to Redis with a 5-minute absolute expiration and 1-minute sliding expiration
await _cache.SetRecordAsync(
cacheKey,
product,
absoluteExpireTime: TimeSpan.FromMinutes(5),
slidingExpireTime: TimeSpan.FromMinutes(1));
}
return Ok(product);
}
private static async Task<Product?> FetchProductFromDatabaseAsync(int id)
{
// Simulated database call
await Task.Delay(100);
return new Product { Id = id, Name = "Mechanical Keyboard", Price = 99.99m };
}
}
public record Product
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
}5. Invalidate or Remove Cached Items
When an entity is updated or deleted in the database, clear the corresponding cache key to prevent stale data:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
// Perform database deletion logic here...
// Invalidate the cache
string cacheKey = $"Product_{id}";
await _cache.RemoveAsync(cacheKey);
return NoContent();
}