1. Introduction
As API traffic scales, even optimized SQL queries can cause latency and performance bottlenecks under heavy load. Caching is one of the most effective strategies to reduce database hits and improve response time.
ASP.NET Core offers two primary caching mechanisms:
In-Memory Caching (MemoryCache): Keeps data within the application memory.
Distributed Caching (Redis): Stores data centrally, accessible across multiple servers.
By combining these two caching approaches, we can build a hybrid caching strategy that leverages both speed and scalability.
2. Why Caching Matters in APIs
When implemented properly, caching can:
Decrease response time by returning frequently requested data from memory instead of the database.
Reduce database load, preventing query contention and bottlenecks.
Improve scalability, allowing the system to handle high traffic more efficiently.
Minimize external API costs, when third-party API results are cached.
3. Common Caching Layers
| Caching Type | Description | Scope |
|---|---|---|
| MemoryCache | Stores data in the application's local memory. | Single server |
| Redis Cache | Distributed cache accessible across servers or containers. | Multi-server |
| Response Caching | Caches entire HTTP responses. | Web Layer |
| EF Core Second-Level Cache | Caches EF Core query results. | Data Layer |
4. Two-Tier Caching Strategy (MemoryCache + Redis)
In a hybrid approach:
MemoryCache is used for frequently accessed ("hot") data.
Redis is used as a shared cache between multiple API servers.
When a request comes in:
The system checks MemoryCache first.
If not found, it checks Redis.
If not found in Redis either, it fetches data from the database, caches it in both layers, and returns it.
5. Technical Workflow (Flowchart)
┌───────────────────────────────┐
│ API Request │
└──────────────┬────────────────┘
│
▼
┌────────────────────────┐
│ Check MemoryCache │
└──────────────┬─────────┘
│
┌──────────────┴─────────────┐
│ Found? → Return from cache │
└──────────────┬─────────────┘
│
▼
┌────────────────────────┐
│ Check Redis Cache │
└──────────────┬─────────┘
│
┌──────────────┴─────────────┐
│ Found? → Store in MemoryCache│
│ and Return Response │
└──────────────┬─────────────┘
│
▼
┌────────────────────────┐
│ Fetch from Database │
└──────────────┬─────────┘
│
▼
┌────────────────────────┐
│ Store in Redis + Memory │
│ Return Response │
└────────────────────────┘
6. Implementation in ASP.NET Core
Let’s go step by step to implement caching using both Redis and MemoryCache.
Step 1: Install Required Packages
Install the following NuGet packages:
dotnet add package Microsoft.Extensions.Caching.Memory
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package StackExchange.Redis
Step 2: Configure Caching in Program.cs
var builder = WebApplication.CreateBuilder(args);
// Memory Cache
builder.Services.AddMemoryCache();
// Redis Cache
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379"; // Replace with your Redis connection string
options.InstanceName = "MyApp_";
});
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Step 3: Create a Cache Service Interface
public interface ICacheService
{
Task<T> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan duration);
Task RemoveAsync(string key);
}
Step 4: Implement Hybrid Cache Service (Redis + MemoryCache)
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;
public class HybridCacheService : ICacheService
{
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache _distributedCache;
public HybridCacheService(IMemoryCache memoryCache, IDistributedCache distributedCache)
{
_memoryCache = memoryCache;
_distributedCache = distributedCache;
}
public async Task<T> GetAsync<T>(string key)
{
// Step 1: Check MemoryCache
if (_memoryCache.TryGetValue(key, out T value))
return value;
// Step 2: Check Redis
var cachedData = await _distributedCache.GetStringAsync(key);
if (!string.IsNullOrEmpty(cachedData))
{
value = JsonSerializer.Deserialize<T>(cachedData);
// Store in MemoryCache for faster next access
_memoryCache.Set(key, value, TimeSpan.FromMinutes(2));
return value;
}
return default;
}
public async Task SetAsync<T>(string key, T value, TimeSpan duration)
{
// Cache in both Memory and Redis
_memoryCache.Set(key, value, duration);
var data = JsonSerializer.Serialize(value);
await _distributedCache.SetStringAsync(key, data, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = duration
});
}
public async Task RemoveAsync(string key)
{
_memoryCache.Remove(key);
await _distributedCache.RemoveAsync(key);
}
}
Step 5: Register in Dependency Injection
builder.Services.AddScoped<ICacheService, HybridCacheService>();

Join the conversation! Your thoughts help the community grow.