Introduction: Why Do We Need Caching?
Imagine you own a busy coffee shop. If every time a customer asked for the menu, you had to run to the back office, print a new copy, and bring it to the front counter, your line would quickly stretch out the door! Instead, you keep a stack of printed menus right at the counter.
In software development, caching works exactly the same way. When users visit your ASP.NET Core web application—whether your servers are hosted locally in Mumbai, globally in New York, or in the cloud—fetching data repeatedly from a database can slow down your app and frustrate users. Caching allows you to store frequently accessed data in the server's local memory temporarily. The next time a user requests that data, your app grabs it instantly from the "front counter" (the cache) instead of running back to the "office" (the database).
In this tutorial, we will explore how to implement in-memory caching in ASP.NET Core using IMemoryCache. We'll keep it simple, practical, and step-by-step.
Step 1: Add the Memory Cache Service
Modern ASP.NET Core applications are set up in the Program.cs file. Before we can use the cache in our application, we need to tell ASP.NET Core to make the caching service available. We do this using Dependency Injection.
Open your Program.cs file and add this single line of code right before you build the app:
C#
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// STEP 1: Enable In-Memory Caching
builder.Services.AddMemoryCache();
var app = builder.Build();
What is happening here? By calling AddMemoryCache(), you are instructing the ASP.NET Core framework to create an in-memory cache and make the IMemoryCache interface available to the rest of your application.
Step 2: Injecting IMemoryCache into Your Controller
Now that the cache is ready, we need to use it inside our application. Let's say we have an API that fetches a list of products. We will inject the IMemoryCache into our ProductsController.
C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IMemoryCache _cache;
// STEP 2: Inject the cache through the constructor
public ProductsController(IMemoryCache cache)
{
_cache = cache;
}
}
What is happening here?
Through the magic of Dependency Injection, ASP.NET Core automatically provides an instance of IMemoryCache when it creates this controller. We store it in a private variable _cache so we can use it in our API methods.
Step 3: Writing the Caching Logic (Get or Create)
This is where the actual magic happens. When a user requests the list of products, we want to check the cache first.
If the data is there, we return it immediately.
If the data is NOT there, we fetch it from the database, save it to the cache for next time, and then return it.
Here is how you write that logic:
C#
[HttpGet("get-products")]
public IActionResult GetProducts()
{
string cacheKey = "productList";
// Try to get the data from the cache first
if (!_cache.TryGetValue(cacheKey, out List<string> products))
{
// Data is not in the cache! Let's simulate a slow database call.
products = FetchProductsFromDatabase();
// Save the data to the cache so it's there next time
_cache.Set(cacheKey, products);
}
return Ok(products);
}
// A fake method to simulate a slow database
private List<string> FetchProductsFromDatabase()
{
System.Threading.Thread.Sleep(3000); // Simulates a 3-second delay
return new List<string> { "Laptop", "Smartphone", "Headphones" };
}
What is happening here?
We use _cache.TryGetValue(). This method checks for our cacheKey (which we named "productList"). If it finds it, it puts the data into our products variable and skips the database call. If it doesn't find it, it runs the slow database method and then uses _cache.Set() to save it for future requests.
Step 4: Setting Cache Expiration Policies (Crucial!)
If you leave data in the cache forever, users might see outdated information. For example, if a product price changes in the database, the cache won't know unless we tell it to expire.
ASP.NET Core gives us MemoryCacheEntryOptions to set expiration rules. There are two main types:
Absolute Expiration: The cache expires at a specific time, no matter what (e.g., exactly 5 minutes from now).
Sliding Expiration: The cache expires if it hasn't been requested for a certain amount of time. If someone requests it, the timer resets.
Let's upgrade our previous _cache.Set() code to include these rules:
C#
[HttpGet("get-smart-products")]
public IActionResult GetSmartProducts()
{
string cacheKey = "smartProductList";
if (!_cache.TryGetValue(cacheKey, out List<string> products))
{
products = FetchProductsFromDatabase();
// STEP 4: Define expiration rules
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10)) // Dies after 10 mins guaranteed
.SetSlidingExpiration(TimeSpan.FromMinutes(2)); // Dies if nobody asks for it for 2 mins
// Save data with the rules applied
_cache.Set(cacheKey, products, cacheOptions);
}
return Ok(products);
}
Best Practice Tip: It is usually a good idea to mix both! Using a sliding expiration keeps popular data in memory, while an absolute expiration ensures the data eventually refreshes so it doesn't get totally out of sync with your database.
Summary
Implementing IMemoryCache in ASP.NET Core is one of the easiest and most effective ways to optimize web application performance and improve API response times.
Quick Recap:
Enable it in Program.cs using builder.Services.AddMemoryCache().
Inject IMemoryCache into your controllers.
Check the cache using TryGetValue().
Update the cache using Set().
Always use MemoryCacheEntryOptions to prevent stale data.
By taking these few steps, you drastically reduce server load and provide a lightning-fast experience for your users. Happy coding!