Overview
When and how to use IMemoryCache, IDistributedCache, Redis, and HTTP response caching in ASP.NET Core.
The Caching Hierarchy
Modern applications use multiple cache levels. L1 (in-process memory) is fastest but limited and not shared across instances. L2 (distributed Redis) is shared and survives restarts. HTTP caching (CDN/browser) offloads the server entirely.
For most CRUD APIs, Redis distributed caching provides the best balance: sub-millisecond reads, shared state across instances, and persistence across deployments.
- L1 IMemoryCache: microsecond access, process-local
- L2 IDistributedCache/Redis: millisecond, shared
- Response cache: HTTP 304 saves full round-trip
- CDN caching: edge serves requests near users
Redis Cache Patterns
Cache-aside is the most common pattern: check cache first, fetch from DB on miss, populate cache. For high-read low-write data, read-through caching transparently handles this.
Always set appropriate TTLs. Use sliding expiration for user session data and absolute expiration for reference data.
services.AddStackExchangeRedisCache(options =>
options.Configuration = configuration.GetConnectionString("Redis"));
// Cache-aside pattern
var user = await _cache.GetAsync<User>($"user:{id}");
if (user is null)
{
user = await _db.Users.FindAsync(id);
await _cache.SetAsync($"user:{id}", user, TimeSpan.FromMinutes(10));
}
return user;Key Takeaways
- Always invalidate cache on write operations
- Use cache stampede protection (SemaphoreSlim or locking)
- Redis TTLs prevent stale data without explicit invalidation
- Monitor cache hit rates—below 80% suggests misconfiguration
- IMemoryCache has size limits—set them to prevent OOM
Saurav Rai
Founder & Lead Architect, Omni Stack
7+ years building enterprise .NET and cloud applications for clients across Australia, USA, and the Middle East. Passionate about clean architecture, developer experience, and shipping fast.