IMemoryCache: lives in process memory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // ====================================================================== // Q: Which caching options exist in ASP.NET Core, and when do you use each? // // - IMemoryCache: lives in process memory. For a single instance and small data // that rarely changes (settings, country lists). // - IDistributedCache (Redis): external, shared by all instances. For multiple // instances, sessions, and data that must be identical everywhere. // - HybridCache (.NET 9): L1 in memory + L2 in Redis. The new default: stampede // protection and tags for invalidation. // - Output caching: the whole HTTP response, on the server. For public pages or // APIs (catalog) that are the same for everyone. // - HTTP caching (Cache-Control, ETag): browser, proxy. You avoid the whole // request or send 304 Not Modified. // - CDN (Azure Front Door): edge servers. Static assets, product images. // Output caching does not cache authenticated requests by default. In B2B every // customer has their own prices, so the response is not the same for everyone. // There you cache the shared data (catalog) and compute the price per customer // separately. // ====================================================================== // HybridCache: automatically uses Redis as L2 if it is registered builder.Services.AddStackExchangeRedisCache(o => o.Configuration = builder.Configuration.GetConnectionString("Redis")); builder.Services.AddHybridCache(o => o.DefaultEntryOptions = new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(10), // L2 (Redis) LocalCacheExpiration = TimeSpan.FromMinutes(1) // L1 (memory) }); public class CatalogService(HybridCache cache, AppDbContext db) { public ValueTask<ProductDto?> GetProductAsync(string sku, CancellationToken ct) => cache.GetOrCreateAsync( $"product:{sku}", async token => await db.Products.AsNoTracking() .Where(p => p.Sku == sku) .Select(p => new ProductDto(p.Sku, p.Name, p.ListPrice)) .FirstOrDefaultAsync(token), tags: ["catalog"], cancellationToken: ct); } // Output caching for a public catalog builder.Services.AddOutputCache(o => o.AddPolicy("catalog", b => b .Expire(TimeSpan.FromMinutes(5)) .SetVaryByQuery("category", "page") .Tag("catalog"))); app.UseOutputCache(); app.MapGet("/api/catalog", GetCatalog).CacheOutput("catalog"); |