Simple In-Memory Cache in .NET Core
Why use caching?
Consider a scenario where we need to get lots of data from an application at a frequent rate. To get that data, our application may need to make a call to a database, an external system or a cloud storage. The data changes infrequently. If we consider using caching, the next time this data is requested, our application can just read it from the cache and return it.
Caching can improve the performance and scalability of our applications by reducing the work needed to generate content.
.NET Core In-Memory Cache
A very simple cache implementation in .NET Core is based on IMemoryCache. The data is cached in the memory of the application, which allows for fast reading of the same. Note that if the server on which the application is running is restarted, the cache would be flushed.
For further details on this topic, I recommend you read the official Microsoft Docs: Cache in-memory in ASP.NET Core | Microsoft Docs
Project Structure
Using the IMemoryCache
The goal of this example will be to use the IMemoryCache to cache some data in memory, read and return it on consecutive API calls. In our case, we’ll want to cache a list of categories (we assume that this data changes infrequently).
We have a very simple API call, which returns a list of categories.
[ApiController]
[Route("[controller]")]
public class CategoryController : ControllerBase
{
public ICategoryService _categoryService { get; set; }
public CategoryController(ICategoryService categoryService)
{
_categoryService = categoryService;
}
[HttpGet(Name = "Categories")]
public List<Category> GetAll()
=> _categoryService.GetCategories();
}
For a simpler example, we won’t be making any calls to a real database. Instead, we’ll use a mock implementation of a repository.
Recommended by LinkedIn
public class CategoryRepository : ICategoryRepository
{
public List<Category> GetCategories()
{
return new List<Category>
{
new Category { Name = "Music", Description = "Best music for you to listen to"},
new Category { Name = "Movies", Description = "Watch the top movies in the world"},
new Category { Name = "Games", Description = "Best selling games right not"}
};
}
}
Before we combine this code all together in the service, for us to be able to use IMemoryCache, we need to install the following NuGet Package: Microsoft.Extensions.Caching.Memory.
Last thing to do is add the following line of code in the Program.cs file:
builder.Services.AddMemoryCache();
Now, it’s time to implement our service, which will be responsible for returning the required data.
public class CategoryService : ICategoryService
{
public ICategoryRepository _categoryRepository;
private readonly IMemoryCache _memoryCache;
private const string CategoryCacheKey = "Categories";
public CategoryService(ICategoryRepository categoryRepository, IMemoryCache memoryCache)
{
_categoryRepository = categoryRepository;
_memoryCache = memoryCache;
}
public List<Category> GetCategories()
{
var cachedResult = _memoryCache.Get<List<Category>>(CategoryCacheKey);
if (cachedResult == null)
{
var categories = _categoryRepository.GetCategories();
_memoryCache.Set(CategoryCacheKey, categories);
return categories;
}
return cachedResult;
}
}
The code above is pretty easy to follow.
The first thing it does is check if there is a value cached in-memory for the given cache key. If the cachedResult is null, it means that there is no cached data for the given cache key. So, it calls the mock repository, and gets the list of categories. Then, it caches that same list in-memory, for the given cache key and returns the result.
Now, if we make consecutive requests to the same API call, we’ll see that the cachedResult will not be empty, so the service will just return the in-memory cached result.
If we compare a non-cached result with a cached one, we can see an improvement in the execution time from 48ms to 9ms.
The full, more detailed implementation of this example can be found on my GitHub repository on the following link: