Unified caching abstraction with multiple provider support (Redis, Dapr, .NET Core). Provides consistent API for cache operations with GetOrSet pattern and configurable expiration.
services.AddRedisDistributedCache();
services.AddSingleton<IConnectionMultiplexer>(sp =>
ConnectionMultiplexer.Connect(configuration.GetConnectionString("Redis")));services.AddDaprDistributedCache("statestore");services.AddNetCoreDistributedCache(services =>
{
services.AddStackExchangeRedisCache(options =>
options.Configuration = configuration.GetConnectionString("Redis"));
});public class ProductService
{
private readonly IDistributedCacheService _cache;
public async Task<Product?> GetProductAsync(Guid id)
{
return await _cache.GetAsync<Product>($"product:{id}");
}
public async Task SetProductAsync(Product product)
{
await _cache.SetAsync($"product:{product.Id}", product,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
}
}public async Task<Product> GetProductAsync(Guid id)
{
return await _cache.GetOrSetAsync(
$"product:{id}",
async () => await _repository.GetAsync(id),
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(30)
});
}public async Task UpdateProductAsync(Product product)
{
await _repository.UpdateAsync(product);
await _cache.RemoveAsync($"product:{product.Id}");
}// Absolute - Expires at specific time
new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(1)
}
// Relative - Expires after duration
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
}
// Sliding - Resets on access
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromMinutes(15)
}public interface IDistributedCacheService
{
Task<T?> GetAsync<T>(string key, CancellationToken ct = default) where T : class;
Task SetAsync<T>(string key, T value, DistributedCacheEntryOptions? options = null, CancellationToken ct = default) where T : class;
Task RemoveAsync(string key, CancellationToken ct = default);
Task RefreshAsync(string key, CancellationToken ct = default);
Task<T?> GetOrSetAsync<T>(string key, Func<Task<T>> fetchFunc, DistributedCacheEntryOptions? options = null, CancellationToken ct = default) where T : class;
}- Use consistent key naming -
{type}:{id}format (e.g.,product:123) - Prefer GetOrSet - Handles cache miss automatically
- Choose appropriate expiration - Sliding for sessions, absolute for data
- Invalidate on updates - Remove stale data after modifications
- Distributed Lock - Coordinate cache updates