Caching Strategies (MemoryCache, Distributed Cache, Redis)
Speed up your .NET applications by serving data from fast caches instead of hitting the database on every request.
What You'll Learn
- • In-memory caching with
IMemoryCache - • Sliding vs absolute expiration strategies
- • Distributed caching with Redis via
IDistributedCache - • Cache invalidation and the
GetOrCreateAsyncpattern
🍽️ Real-World Analogy
In-memory cache is like keeping today's specials on a whiteboard at the kitchen counter — instant to read but lost if the kitchen closes. Distributed cache (Redis) is like a shared recipe binder in a chain of restaurants — every location reads from the same source, and updates propagate to all kitchens.
In-Memory Caching with IMemoryCache
IMemoryCache stores data in the application's memory. It's the fastest cache option but is lost on restart and isn't shared across servers. Use sliding expiration to keep frequently accessed items alive, and absolute expiration to set a hard limit.
IMemoryCache with Expiration & Eviction
Cache products with sliding and absolute expiration, plus eviction callbacks.
using Microsoft.Extensions.Caching.Memory;
// Program.cs — register IMemoryCache
builder.Services.AddMemoryCache();
// ProductService.cs
public class ProductService
{
private readonly IMemoryCache _cache;
private readonly AppDbContext _db;
public ProductService(IMemoryCache cache, AppDbContext db)
{
_cache = cache;
_db = db;
}
public async Task<Product?> GetProductAsync(int id)
{
string cacheKey = $"product_{id}";
// Try get from c
...The GetOrCreateAsync Pattern
GetOrCreateAsync is the cleanest caching pattern — it checks the cache, and if the item doesn't exist, runs your factory delegate to create it. Thread-safe and concise.
GetOrCreateAsync Pattern
Use the elegant GetOrCreate pattern for thread-safe cache population.
using Microsoft.Extensions.Caching.Memory;
public class CategoryService
{
private readonly IMemoryCache _cache;
private readonly AppDbContext _db;
public CategoryService(IMemoryCache cache, AppDbContext db)
{
_cache = cache;
_db = db;
}
// GetOrCreateAsync — elegant pattern
public async Task<List<Category>> GetAllCategoriesAsync()
{
return await _cache.GetOrCreateAsync("all_categories", async entry =>
{
entry.SetAbsol
...Distributed Caching with Redis
When your app runs on multiple servers, in-memory cache isn't shared. IDistributedCache with Redis gives you a shared, persistent cache that survives restarts. You can also use response caching to cache entire HTTP responses.
Redis Distributed Cache & Response Caching
Store sessions in Redis and cache HTTP responses with middleware.
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
// Program.cs — Redis distributed cache
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
options.InstanceName = "MyApp_";
});
public class SessionService
{
private readonly IDistributedCache _cache;
public SessionService(IDistributedCache cache)
{
_cache = cache;
}
// Store complex object in Redis
public async Task SetUserSessionAsy
...Pro Tip
Use cache stampede prevention: when a popular cache entry expires, hundreds of requests may hit the database simultaneously. Use SemaphoreSlim or a library like FusionCache to let only one request populate the cache while others wait.
Common Mistakes
- • Caching mutable objects — other code can modify the cached reference
- • No cache invalidation — stale data persists after updates
- • Using only sliding expiration — items that are always accessed never refresh
Lesson Complete!
You've mastered caching strategies in .NET. Next, build real-time features with SignalR.
Sign up for free to track which lessons you've completed and get learning reminders.