Daily Knowledge Drop
ASP.NET Core has out of the box configurable response caching
functionality which can be leveraged to improve the performance of a service using controllers
.
The functionality described in this post is only for caching responses from an MVC controller - to cache responses from a minimal API, have a look at this post which details output caching on minimal APIs.
Code
Setup
The default template Weather API project is used for the below example, with the Use Controllers
options checked.
Startup
First step, enabling the caching functionality
in the service - this is done in the program.cs, and involves adding two lines of code:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// setup caching with the DI container
builder.Services.AddResponseCaching();
var app = builder.Build();
// setup caching in the pipeline
app.UseResponseCaching();
app.UseAuthorization();
app.MapControllers();
app.Run();
This follows a fairly standard pattern when it comes to adding functionality to an ASP.NET Core application:
- The
AddResponseCaching
method is called to register the required services with the dependency injection container - The
UseResponseCaching
method is called to insert the caching logic into the middleware pipeline
Next step, enabling caching on a controller.
Controller
Now that we have the base caching functionality configured in the service, the next step is to actual enable caching for a specific endpoint/controller. This is done by adding an attribute to the relevent controller method
. In the WeatherForecastController.cs class:
[HttpGet]
[ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any)]
public IEnumerable<WeatherForecast> Get()
{
Console.WriteLine($"{nameof(Get)} method in {nameof(WeatherForecastController)} called");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
The ResponseCache
attribute is added to the Get method (the method called when the endpoint is called). In this example, the cache is set to expire every 30 seconds.
And thats it! We now have basic caching functionality working on the service.
Output
If we run the service and browse to the /weatherforecast
endpoint, the weather payload will be returned, and logging at the console, the following will be output:
Get method in WeatherForecastController called
Refreshing the endpoint within 30 seconds (the duration of the cache), will yield the same payload, and cause no output to the console - the results are returned from the cache, and the controller is never called.
Vary
Not explicitly show in the above example, but it is also possible to vary the cache response
by a specific key.
If the attribute parameters were changed to include the VaryByHeader
parameter:
[HttpGet]
[ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any, VaryByHeader = "User-Agent")]
public IEnumerable<WeatherForecast> Get()
{
Console.WriteLine($"{nameof(Get)} method in {nameof(WeatherForecastController)} called");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
Then a different cache is created for each distinct User-Agent
header value sent on a request, each cache independent of one another. This way, the cache of one called will operate independently from the cache of a different caller. It is also possible to vary the cache by a specific query string key
.
Notes
When simple caching is required, the built-in functionality is an easy to implement, low effort option to enhance the performance of a service.
References
Daily Drop 231: 10-01-2023
At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.
The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.On This Page