Overview
MvcCore Utilidades provides two caching approaches to improve application performance:
- Memory Cache (In-Memory): Server-side caching with custom expiration settings
- Distributed Cache (Response Caching): Client-side caching using HTTP response cache headers
The CachingController demonstrates both implementations.
Key Components
CachingController
Controller demonstrating memory cache and distributed cache implementations.
Namespace: MvcNetCoreUtilidades.Controllers
Dependencies:
IMemoryCache - Injected via constructor for in-memory caching
Configuration
Register memory cache
Add memory cache services in Program.cs:var builder = WebApplication.CreateBuilder(args);
// Register memory cache
builder.Services.AddMemoryCache();
builder.Services.AddControllersWithViews();
Enable distributed memory cache (optional)
For session support and distributed caching:// Add distributed memory cache for sessions
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();
var app = builder.Build();
// Enable session middleware
app.UseSession();
Memory Cache Implementation
MemoriaPersonalizada Action
Custom memory caching with configurable expiration time.
public IActionResult MemoriaPersonalizada(int? tiempo)
{
if (tiempo == null)
{
tiempo = 60;
}
string fecha = DateTime.Now.ToLongDateString() + "--" + DateTime.Now.ToLongTimeString();
if (this.memoryCache.Get("FECHA") == null)
{
// Cache miss - store new value
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(tiempo.Value));
this.memoryCache.Set("FECHA", fecha, options);
ViewData["MENSAJE"] = "Fecha almacenada correctamente";
ViewData["FECHA"] = this.memoryCache.Get("FECHA");
}
else
{
// Cache hit - retrieve existing value
fecha = this.memoryCache.Get<string>("FECHA");
ViewData["MENSAJE"] = "Fecha recuperada correctamente";
ViewData["FECHA"] = fecha;
}
return View();
}
Features:
- Configurable expiration time via query parameter
- Defaults to 60 seconds if not specified
- Absolute expiration (cache expires after fixed duration)
- Provides feedback on cache hit/miss
Usage:
GET /Caching/MemoriaPersonalizada?tiempo=120
Distributed Cache Implementation
MemoriaDistribuida Action
Client-side caching using HTTP response cache headers.
[ResponseCache(Duration = 15, Location = ResponseCacheLocation.Client)]
public IActionResult MemoriaDistribuida()
{
string fecha = DateTime.Now.ToLongDateString() + "--" + DateTime.Now.ToLongTimeString();
ViewData["FECHA"] = fecha;
return View();
}
Features:
- Caches response on the client (browser) for 15 seconds
- Reduces server load by serving cached content from browser
- Automatic cache invalidation after duration expires
- No server-side storage required
IMemoryCache Methods
Get
Retrieves a cached value by key.
object value = memoryCache.Get("KEY");
Get<T>
Retrieves a strongly-typed cached value.
string value = memoryCache.Get<string>("FECHA");
int number = memoryCache.Get<int>("COUNT");
Set
Stores a value in cache with options.
memoryCache.Set("KEY", value, options);
TryGetValue
Attempts to retrieve a value, returns boolean indicating success.
if (memoryCache.TryGetValue("KEY", out string value))
{
// Value exists in cache
Console.WriteLine(value);
}
else
{
// Value not in cache
}
Remove
Removes a value from cache.
memoryCache.Remove("KEY");
Cache Entry Options
Absolute Expiration
Cache expires after a fixed duration from creation.
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(60));
Sliding Expiration
Cache expires after period of inactivity.
var options = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
// Resets expiration timer on each access
Expiration at Specific Time
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(DateTimeOffset.Now.AddHours(2));
Cache Priority
var options = new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.High)
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
Priority Levels:
CacheItemPriority.Low - First to be removed under memory pressure
CacheItemPriority.Normal - Default priority
CacheItemPriority.High - Last to be removed
CacheItemPriority.NeverRemove - Never removed automatically
Complete Examples
Example 1: Product Cache Service
using Microsoft.Extensions.Caching.Memory;
public class ProductCacheService
{
private readonly IMemoryCache _cache;
private readonly IProductRepository _repository;
private const string PRODUCT_CACHE_KEY = "ALL_PRODUCTS";
public ProductCacheService(IMemoryCache cache, IProductRepository repository)
{
_cache = cache;
_repository = repository;
}
public async Task<List<Product>> GetProductsAsync()
{
// Try to get from cache
if (_cache.TryGetValue(PRODUCT_CACHE_KEY, out List<Product> products))
{
return products;
}
// Cache miss - load from database
products = await _repository.GetAllAsync();
// Set cache options
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10))
.SetPriority(CacheItemPriority.High);
// Store in cache
_cache.Set(PRODUCT_CACHE_KEY, products, cacheOptions);
return products;
}
public void InvalidateCache()
{
_cache.Remove(PRODUCT_CACHE_KEY);
}
}
public class ProductsController : Controller
{
private readonly ProductCacheService _cacheService;
public ProductsController(ProductCacheService cacheService)
{
_cacheService = cacheService;
}
public async Task<IActionResult> Index()
{
var products = await _cacheService.GetProductsAsync();
return View(products);
}
[HttpPost]
public IActionResult Create(Product product)
{
// Save product...
// Invalidate cache
_cacheService.InvalidateCache();
return RedirectToAction(nameof(Index));
}
}
Example 2: User Session Cache
public class UserSessionService
{
private readonly IMemoryCache _cache;
public UserSessionService(IMemoryCache cache)
{
_cache = cache;
}
public void StoreUserSession(string userId, UserSessionData data)
{
var options = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(30))
.SetAbsoluteExpiration(TimeSpan.FromHours(2))
.RegisterPostEvictionCallback((key, value, reason, state) =>
{
// Log session expiration
Console.WriteLine($"Session expired for user {key}: {reason}");
});
_cache.Set($"session_{userId}", data, options);
}
public UserSessionData GetUserSession(string userId)
{
return _cache.Get<UserSessionData>($"session_{userId}");
}
public void RemoveUserSession(string userId)
{
_cache.Remove($"session_{userId}");
}
}
Example 3: Response Cache Configuration
Controller Level
Action Level
// Cache all actions for 60 seconds
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
public class StaticContentController : Controller
{
public IActionResult Index()
{
return View();
}
// Override for specific action
[ResponseCache(Duration = 300)]
public IActionResult LongLived()
{
return View();
}
// Disable caching for specific action
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult Dynamic()
{
return View();
}
}
public class ContentController : Controller
{
// Client-side cache for 5 minutes
[ResponseCache(Duration = 300, Location = ResponseCacheLocation.Client)]
public IActionResult ClientCache()
{
return View();
}
// Server-side cache (proxy/CDN)
[ResponseCache(Duration = 3600, Location = ResponseCacheLocation.Any)]
public IActionResult ServerCache()
{
return View();
}
// Cache with VaryByQueryKeys
[ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "id", "category" })]
public IActionResult Details(int id, string category)
{
return View();
}
}
Response Cache Locations
| Location | Description | Use Case |
|---|
Client | Browser cache only | User-specific content |
Any | Browser, proxy, and server | Public content |
None | No caching | Dynamic/sensitive data |
Cache Invalidation Patterns
Time-Based Invalidation
public class CacheManager
{
private readonly IMemoryCache _cache;
// Clear all cache at midnight
public void ScheduleClearCache()
{
var midnight = DateTime.Today.AddDays(1);
var timeUntilMidnight = midnight - DateTime.Now;
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(timeUntilMidnight)
.RegisterPostEvictionCallback((key, value, reason, state) =>
{
// Reload cache for next day
LoadFreshData();
});
}
}
Event-Based Invalidation
public class DataService
{
private readonly IMemoryCache _cache;
public void UpdateData(Data newData)
{
// Update database
_repository.Update(newData);
// Invalidate related cache entries
_cache.Remove($"data_{newData.Id}");
_cache.Remove("all_data");
_cache.Remove($"category_{newData.CategoryId}");
}
}
Best Practices
Choose the right cache type: Use memory cache for server-side data, response cache for client-side content.
Memory limits: In-memory cache uses server RAM. Monitor memory usage and set appropriate size limits.
When to Use Memory Cache
- Database query results
- Computed/expensive operations
- Configuration data
- Session data
- Frequently accessed objects
When to Use Response Cache
- Static HTML pages
- Public content (same for all users)
- CSS/JS bundles
- Images and media
- API responses that don’t change frequently
- Set appropriate expiration times - Not too short (frequent misses) or too long (stale data)
- Use sliding expiration for frequently accessed data
- Implement cache warming for critical data
- Monitor cache hit ratio to measure effectiveness
- Use cache keys strategically - Include version/timestamp when needed
// Good: Versioned cache key
string cacheKey = $"products_v{APP_VERSION}_{categoryId}";
// Good: User-specific cache
string cacheKey = $"cart_{userId}";
// Avoid: Generic keys that may conflict
string cacheKey = "data"; // Too generic
Common Patterns
Cache-Aside Pattern
public async Task<Product> GetProductAsync(int id)
{
string cacheKey = $"product_{id}";
// 1. Check cache
if (_cache.TryGetValue(cacheKey, out Product product))
return product;
// 2. Load from database
product = await _repository.GetByIdAsync(id);
// 3. Store in cache
if (product != null)
{
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
_cache.Set(cacheKey, product, options);
}
return product;
}
Write-Through Pattern
public async Task UpdateProductAsync(Product product)
{
// 1. Update database
await _repository.UpdateAsync(product);
// 2. Update cache
string cacheKey = $"product_{product.Id}";
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
_cache.Set(cacheKey, product, options);
}
Source Reference
- CachingController:
Controllers/CachingController.cs:6
- MemoriaPersonalizada action:
Controllers/CachingController.cs:19
- MemoriaDistribuida action:
Controllers/CachingController.cs:43
- Program.cs configuration:
Program.cs:12