In this post, we will see how to use In-Memory Caching to improve the performances of our Minimal APIs.
But, why should we use caching?
“Caching temporarily stores copies of data so future requests for that data can be served faster. The idea is to reduce the number of expensive calls, such as database queries, making the overall experience quicker for the user and less resource-intensive for the server.”
“MemoryCache” is a built-in .NET feature that lets us store objects in memory.
It’s perfect for data that doesn’t change often but is requested frequently. By implementing “MemoryCache”, we can significantly reduce the load on your database and improve response times for your users.
For this post, we will use the same project used in all Minimal APIs’ post.
First of all, we modify the method GettAllDogs in the file DogCommands.cs:
public async Task LoadDefaultValuesDB()
{
List<Dog> lstDogs = new List<Dog>();
lstDogs.Add(new Dog { Id = 1, Name = "Max", Breed = "Labrador Retriever", Color = "Yellow" });
lstDogs.Add(new Dog { Id = 2, Name = "Bella", Breed = "German Shepherd", Color = "Black" });
lstDogs.Add(new Dog { Id = 3, Name = "Charlie", Breed = "Beagle", Color = "Tri-color" });
lstDogs.Add(new Dog { Id = 4, Name = "Lucy", Breed = "Poodle", Color = "White" });
lstDogs.Add(new Dog { Id = 5, Name = "Cooper", Breed = "Bulldog", Color = "Brindle" });
lstDogs.Add( new Dog { Id = 6, Name = "Daisy", Breed = "Boxer", Color = "Fawn" });
lstDogs.Add( new Dog { Id = 7, Name = "Bailey", Breed = "Rottweiler", Color = "Black and Tan" });
lstDogs.Add( new Dog { Id = 8, Name = "Lola", Breed = "Siberian Husky", Color = "Grey" });
lstDogs.Add( new Dog { Id = 9, Name = "Oliver", Breed = "Dachshund", Color = "Red" });
lstDogs.Add( new Dog { Id = 10, Name = "Sadie", Breed = "Golden Retriever", Color = "Golden" });
foreach (var item in lstDogs)
{
await _dataContext.Dogs.AddAsync(item);
}
await _dataContext.SaveChangesAsync();
}
public async Task<List<Dog>> GetAllDogs()
{
_logger.LogInformation("BLL - Retrieving all dogs");
var lstDogs = await _dataContext.Dogs.AsNoTracking().ToListAsync();
if (!lstDogs.Any())
{
await LoadDefaultValuesDB();
}
return await _dataContext.Dogs.AsNoTracking().ToListAsync();
}
Then, in Program.cs, we add the MemoryCache in our Service:
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using MinimalAPI;
using MinimalAPI.Commands;
using MinimalAPI.Extensions;
using MinimalAPI.Model;
using Serilog;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
Log.Logger = logger;
builder.Host.UseSerilog();
// definition of DataContext
builder.Services.AddDbContext<DataContext>(opt => opt.UseInMemoryDatabase("DbDog"));
// definition of Dependency Injection
builder.Services.AddScoped<IDogCommands, DogCommands>();
// Add services to the container.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthorization();
// Add services to the container.
builder.Services.AddMemoryCache();
...
...
...
Finally, we modify the Get endpoint to implement the “In Memory Caching”:
app.MapGet("/dog", async (IDogCommands commands, ILogger<Program> loggerInput, IMemoryCache cache) =>
{
const string cacheKey = "dogsList";
List<Dog> dogs = null;
// Log the beginning of the request to get all dogs
loggerInput.LogInformation("Requesting all dogs");
// Try to retrieve the cached list of dogs
if (!cache.TryGetValue(cacheKey, out dogs))
{
loggerInput.LogInformation("Cache miss. Fetching dogs from database...");
// Execute the GetAllDogs command to retrieve all dogs
dogs = await commands.GetAllDogs();
// Check if the result is null or empty
if (dogs == null || !dogs.Any())
{
// Log a warning indicating that no dogs were found
loggerInput.LogWarning("No dogs found");
// Return a NotFound result to indicate that no dogs were found
return Results.NotFound();
}
// Set cache with a relative expiration time of 5 minutes
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
cache.Set(cacheKey, dogs, cacheEntryOptions);
loggerInput.LogInformation($"Fetched {dogs.Count} dogs from the database and cached.");
}
else
{
loggerInput.LogInformation($"Retrieved {dogs.Count} dogs from cache.");
}
// Return an Ok result with the list of retrieved dogs
return Results.Ok(dogs);
}).RequireAuthorization();
We are done and now, if we run the APIs, the following will be the result: