ASP.NET Core MVC – Caching Tag Helpers
All posts in ASP.NET Core Tag Helpers series:
- ASP.NET Core MVC – All about Tag Helpers
- ASP.NET Core MVC – Caching Tag Helpers
- ASP.NET Core MVC – Form Tag Helpers
- ASP.NET Core MVC – Custom Tag Helpers
- ASP.NET Core MVC – Tag Helper Components
Introduction
Caching can significantly improve our application load time and responsiveness. What can we cache with cache Tag Helpers? Well, we cache HTML content. We usually cache content that does not change frequently.
In one of the previous posts, we talked about Tag Helpers. We saw what Tag Helpers do, how we can use them and we saw how do we use some of the most used Tag Helpers.
Today, we are going to look at how we can use caching Tag Helpers and what they are good for.
The sample application is available on GitHub – CachingTagHelpers.
Cache Tag Helper
First one is <cache> tag helper. Unlike other Tag Helpers that we talked about that are being used as attributes on other tags, Cache Tag Helper is used like a tag – <cache>. Therefore, it wraps content and makes that content available for caching in memory, based on the options we specify with its attributes.
Let us see an example code:
When we open the page for the first time, we will see the current time. However, if we refresh the page within 30 seconds, we will get same content every time. However, once those 30 seconds from the first hit have passed there will be new content, and we will again see the current time in the output.
We got three options for controlling cache Tag Helper via time-related attributes:
- expires-after – most commonly used one, it will cache content for the specific amount of time (time span)
- expires-on – takes a date time offset
- expires-sliding – every time user requests content rendered caching gets extended by specified amount of time (time span)
vary-by attributes
Cache Tag Helpers build cache keys by generating an ID that is unique for the context of the cache Tag Helper. That means we can have multiple cache tag helpers inside of the same view and that those contents will be unique and will not overlap. We can also instruct caching Tag Helpers to use more complex keys by using one of the vary-by attributes or combination of any of the vary-by attributes.
List of vary by attribute suffixes:
- query
- route
- cookie
- header
- user
- vary-by
vary-by-query
It makes unique caches (keys) based on the query parameters for the current request.
For the following code:
We get the following output and changes on the page:
vary-by-route
This one will make unique caches based on the set of route data parameters. It takes a comma-separated list of route data parameter names.
vary-by-cookie
This one will make unique caches based on the value stored in the cookie. It takes a comma-separated list of cookie names.
vary-by-header
This one will make unique caches based on the set of route data parameters. It takes a single header name.
vary-by-user
This one will make unique caches based on the logged in user. Attribute expects a boolean value.
Can we do more?
As we saw, <cache> Tag Helper is very cool and useful. However, it stores cache entries in memory in the local process. If the server process restarts, all of the cache content will be lost.
Furthermore, if we have multiple servers, every server gets to have its cache. And that is probably not what you want.
You could use something called sticky sessions. Sticky sessions ensure that subsequent requests from a client all go to the same server.
Another way to deal with inconsistent caching over multiple servers is to use distributed caching. That is why folks at ASP.NET team also made <distributed-cache> Tag Helper. Let’s see what is that all about.
Distributed Cache Tag Helper
Why should we use distributed cache at all?
- Support higher scaling (when compared to in-memory cache)
- using distributed cache offloads the cache memory to an external process
- we have coherent data across all of our web servers (users always get the same results, no matter which web server they hit)
- cached data does not get lost on server restarts and during deployments
- we can remove or add web servers without losing cached data
Now we can talk about that distributed cache Tag Helper we mentioned in the previous chapter.
Distributed Cache Tag Helper behaves almost the same as the CacheTag helper. Every attribute available for <cache> Tag Helper is also available for <distributed-cache> Tag Helper.
However, where Distributed Cache TagHelper differs is that it enables us to inject and use an external cache manager instead of using the default in-memory cache manager.
If you want to store the cached HTML content in distributed cache using SQL Server, Redis or something similar then <distributed-cache> TagHelper is the what you need.
Two cache manager implementations that come with ASP.NET Core MVC for distributed caching are SQL Server and Redis.
Another thing where <distributed-cache> differs is that it requires name attribute.
So how do we use distributed cache actually with TagHelper? It is quite easy, but if we just place the <distributed-cache> Tag Helper in Razor code that will not work.
Example with ASP.NET Core MVC 1.1.
Let us add Redis as our distributed cache manager:
- Install and start Redis
- Add following NuGet package:
Microsoft.Extensions.Caching.Redis
- Change the ConfigureServices method by adding the following code:
services.AddDistributedRedisCache(option =>
{
option.Configuration = "localhost";
option.InstanceName = "localRedis";
});
And that is it! From this point, our content inside of <distributed-cache> Tag Helpers should be cached inside of Redis.
Achieving that is even easier with ASP.NET Core 2 since Redis package – Microsoft.Extensions.Caching.Redis
comes with amazing meta package – Microsoft.AspNetCore.All.
Summary
- caching Tag Helpers are quite useful for caching HTML content
- <cache> Tag Helper uses in memory storage and will lose cached content if process of the server restarts
- <distributed-cache> is almost the same as <cache> TagHelper except it requires us to use a name attribute and we can use external cache manager instead of storing cache in memory
- three most important attributes for controlling expiration time are:
- expires-after – most commonly used one, it will cache content for the specific amount of time (timespan)
- expires-on – takes a date time offset
- expires-sliding – every time content is requested and rendered caching gets extended by specified amount of time (timespan)