Cache Tag Helper in ASP.NET Core MVC

Cache Tag Helper in ASP.NET Core MVC

In this article, I will discuss Cache Tag Helper in ASP.NET Core MVC Application with Examples. Please read our previous article discussing View Component Tag Helper in ASP.NET Core MVC.

Cache Tag Helpers in ASP.NET Core MVC

The Cache Tag Helper in ASP.NET Core MVC improves performance by caching the content rendered to the browser. It stores the content in memory for a specified duration, reducing the need to regenerate that content on subsequent requests. This can be especially useful for pages or parts of pages that don’t change frequently.

Cache Tag Helpers use in-memory cache. The content within the tags that use the Cache Tag Helper is dynamically generated once and stored in the cache for subsequent requests until the cache expires.

How Do We Use Cache Tag Helpers in ASP.NET Core MVC?

To use the Cache tag helper, we need to add the cache tag around any block of content that we want to cache. The syntax is given below:

<cache>
    <!-- Your content to cache goes here. -->
</cache>

Note: Cache content is stored in memory by default. You might want to consider distributed caching if your app restarts or runs in a web farm or load-balanced environment.

Cache Tag Helpers Examples in ASP.NET Core MVC:

Let us understand how to use Cache Tag helper in ASP.NET Core MVC Application with an example. First, modify the Home Controller as follows.

using Microsoft.AspNetCore.Mvc;
namespace TagHelpersDemo.Controllers
{
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            return View();
        }
    }
}
Cache Tag Helper in ASP.NET Core MVC:

The Cache Tag Helper improves the performance of ASP.NET Core applications by caching their content. To better understand, please modify the Index view as follows: This will use the default caching behavior, which caches the content inside the cache tag helper for 20 minutes by default.

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Default Caching)</h5>
                    <cache>
                        <p>This section uses default caching settings, which caches content for 20 minutes by default. If no specific attributes like `expires-after`, `expires-sliding`, or `expires-on` are provided, the Cache Tag Helper will use the default expiration time.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

Here, the Cache Tag Helper will use the default caching settings provided by ASP.NET Core. This typically means the content will be cached for 20 minutes by default, and the cache will expire and be refreshed after the default time.

Cache Enabled Attribute:

The enabled attribute of the Cache Tag Helper will allow us to control whether caching is enabled or disabled explicitly. The default value of the enabled attribute is true. If set to false, the rendered output is not cached. For a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Caching Enabled)</h5>
                    <cache enabled="true">
                        <p>This section uses caching because the `enabled` attribute is set to `true`. By default, this will use the system’s default cache duration (typically 20 minutes) unless other attributes are specified.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The enabled attribute is explicitly set to true, which ensures that caching is enabled. No other cache configuration (like expiration or varying by headers) is provided so that it will default to the system’s cache duration (typically 20 minutes). When only the enabled attribute is used, the content inside the Cache Tag Helper will be cached using default settings unless caching is manually disabled by setting enabled=”false.”

Cache Expires-After Attribute in ASP.NET Core MVC:

This attribute sets a duration after which the cached content expires. After this time, the content will be regenerated and re-cached. This is a time-based cache expiration that ensures the content is updated periodically. So, for a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section</h5>
                    <cache expires-after="@TimeSpan.FromMinutes(5)">
                        <p>
                            This section displays content cached for 5 minutes.
                        </p>
                        <p>
                            The content in this block will not be updated for the specified cache duration.
                        </p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>
                        This section always displays the most current content and is not affected by caching.
                    </p>
                    <p>
                        The time displayed here will update every time the page is refreshed.
                    </p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The content within the cache tag will be cached for 5 minutes. After 5 minutes, the content is evicted from the cache and regenerated on the next request.

Cache Expires-Sliding Attribute in ASP.NET Core MVC

The Cache Expires-Sliding Attribute uses sliding expiration for the cache, meaning that the content’s expiration time will be extended each time it is accessed. This allows content to remain in the cache as long as it’s accessed frequently within a defined period. For a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Expires-Sliding)</h5>
                    <cache expires-sliding="@TimeSpan.FromSeconds(120)">
                        <p>This section uses sliding expiration for caching. The content will remain cached for 120 seconds from the last access, meaning that if the content is accessed before the cache expires, the expiration time will reset.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The cached content will remain valid as long as it is accessed within 120 seconds. If it’s accessed again, the 120-second timer is reset. If not, it will expire after 120 seconds of inactivity.

Cache Expires-On Attribute in ASP.NET Core MVC:

The Cache expires-on attribute sets a specific time when the cached content will expire, regardless of how long it has been cached. This approach is useful when you know that content is relevant only until a certain date, and you want to ensure it expires on that specific date. For a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
    var expiryDate = new DateTime(2024, 9, 1);
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Expires on 1st September 2024)</h5>
                    <cache expires-on="@expiryDate">
                        <p>This section caches content and will expire on the 1st of September 2024, regardless of the time it was accessed or when the cache was created.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The expires-on attribute is used to set the specific expiration date for the cache. In this case, we have set it to expire on September 1st, 2024. This attribute ensures that the content will remain cached until that specific date, regardless of any other interactions. The cache content will expire at midnight on September 1st, 2024. After that date, the cache will be cleared, and new content will be generated.

Cache Vary-By-Header Attribute in ASP.NET Core MVC:

The vary-by-header attribute caches different versions of the content based on the value of a specific HTTP header. It is useful when caching depends on headers like User-Agent or Accept-Language. For a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Vary by Header)</h5>
                    <cache expires-after="@TimeSpan.FromMinutes(5)" vary-by-header="User-Agent">
                        <p>This section caches content based on the `User-Agent` header. This means that different versions of the content can be cached for different user agents (e.g., different browsers or devices).</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The vary-by-header attribute is set to User-Agent, meaning the cached content will differ based on the User-Agent header (e.g., Chrome, Firefox, mobile browsers). The cache is still set to expire after 5 minutes using expires-after=”@TimeSpan.FromMinutes(5)”. If no cache duration (expires-after or expires-sliding) is specified, the default cache time is 20 minutes.

Cache Vary-By-Query Attribute in ASP.NET Core MVC:

The vary-by-query attribute in ASP.NET Core MVC caches different versions of the content based on query string parameters. This is ideal for scenarios where content differs based on specific query string values. For a better understanding, please modify the Index view as follows:

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Vary by Query Parameter)</h5>
                    <cache expires-after="@TimeSpan.FromMinutes(5)" vary-by-query="category">
                        <p>This section caches content based on the value of the category query parameter. Different versions of the content will be cached depending on the value of this parameter in the URL.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

The vary-by-query attribute is set to category. This means the cached content will vary based on the value of the category query parameter in the URL. For example, if you access the page with ?category=electrical and then with ?category=merchandise, different cached content will be generated and served for each unique category value.

Note: We don’t need to accept the same query parameter in the controller’s action method for the vary-by-query attribute to work. The vary-by-query attribute operates at the caching level. It will cache different versions of the content based on the query parameter values regardless of whether they are used within the action method.

Using Cache Profile in ASP.NET Core MVC

The cache profile refers to predefined settings such as duration, expiration, etc. It is used to centralize cache settings in one location and apply them by referencing the profile name. First, we need to define the cache Profile in your Program.cs class file as follows. Here, we define the cache profile, which will cache the data for 1 minute.

builder.Services.AddControllersWithViews(options =>
{
    options.CacheProfiles.Add("ShortLivedCache", new CacheProfile()
    {
        Duration = 60 // 1 minute
    });
});

After defining a cache profile in the Program.cs class, use it in your Razor view. So, modify the Index.cshtml view as follows. The content will use the caching rules defined in the ShortLivedCache, including a 60-second cache duration.

@{
    ViewBag.Title = "Index";
}

<div class="container mt-4">
    <div class="row">
        <div class="col-12 mb-4">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Cached Content Section (Using Cache Profile)</h5>
                    <cache cache-profile="ShortLivedCache">
                        <p>This section uses a predefined cache profile for caching. The caching behavior is controlled by the settings specified in the cache profile.</p>
                        <p><strong>Current Time (Inside Cache Block):</strong> @DateTime.Now</p>
                    </cache>
                </div>
            </div>
        </div>
        <div class="col-12">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h5 class="card-title">Non-Cached Content Section</h5>
                    <p>This section always displays the most current content and is not affected by caching. The time displayed here will update every time the page is refreshed.</p>
                    <p><strong>Current Time (Outside Cache Block):</strong> @DateTime.Now</p>
                </div>
            </div>
        </div>
    </div>
</div>

Real-time Example to Understand Cache Tag Helper in ASP.NET Core MVC:

Let us walk through a real-time example using the Cache Tag Helper in an ASP.NET Core MVC project to cache the output of a specific portion of a view. Assume we have a simple web page showing the latest news headlines. We want to cache the output of this news section for 300 seconds (5 minutes) to reduce the load on the server when fetching the latest news.

Create the Models:

First, we create a simple News model to hold the news headline. So, create a class file named News.cs within the Models folder and then copy and paste the following code:

namespace TagHelpersDemo.Models
{
    public class News
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTime PublishedDate { get; set; }
    }
}
Create News Service

Next, we will create a simple service that simulates fetching the latest news from a database or external API. But, here, we will hardcode the data. So, create a class file named NewsService.cs within the Models folder and then copy and paste the following code:

namespace TagHelpersDemo.Models
{
    public class NewsService
    {
        private static readonly List<News> _news = new List<News>
        {
            new News
            {
                Id = 1,
                Title = "New Technology Breakthrough Revolutionizes AI",
                Content = "Scientists have developed a new algorithm that significantly improves the speed and accuracy of machine learning models.",
                PublishedDate = DateTime.Now.AddMinutes(55)
            },
            new News
            {
                Id = 2,
                Title = "Global Climate Summit: Nations Agree on New Targets",
                Content = "At the Global Climate Summit, world leaders have agreed on ambitious targets to reduce carbon emissions and combat climate change.",
                PublishedDate = DateTime.Now.AddMinutes(33)
            },
            new News
            {
                Id = 3,
                Title = "Major Stock Markets Hit Record Highs",
                Content = "Today, global stock markets reached new highs as investor confidence grew in the economic recovery post-pandemic.",
                PublishedDate = DateTime.Now.AddMinutes(24)
            },
            new News
            {
                Id = 4,
                Title = "Breakthrough in Renewable Energy: New Solar Panel Efficiency",
                Content = "Researchers have developed a new type of solar panel that increases energy efficiency by 20%, promising cheaper and cleaner energy.",
                PublishedDate = DateTime.Now.AddMinutes(15)
            }
        };

        // Simulating latest news fetch
        public List<News> GetLatestNews()
        {
            return _news.OrderByDescending(n => n.PublishedDate).ToList();
        }
    }
}
Create News Controller:

The controller will fetch the latest news from the NewsService and pass it to the view. So, create an Empty MVC Controller named NewsController within the Controllers folder and then copy and paste the following code:

using Microsoft.AspNetCore.Mvc;
using TagHelpersDemo.Models;

namespace TagHelpersDemo.Controllers
{
    public class NewsController : Controller
    {
        public IActionResult LatestNews()
        {
            NewsService _newsService = new NewsService();
            var latestNews = _newsService.GetLatestNews();
            return View(latestNews);
        }
    }
}
Create LatestNews View

We will now create the view that displays the latest news and caches the output using the Cache Tag Helper for 120 seconds. So, create a view named LatestNews.cshtml within the Views/News folder and then copy and paste the following code:

@model List<News>
@{
    ViewData["Title"] = "Latest News";
}

<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <h2 class="text-center mb-4">Latest News</h2>

            <cache expires-after="@TimeSpan.FromMinutes(5)">
                @foreach (var newsItem in Model)
                {
                    <div class="card mb-4 shadow border-0">
                        <div class="card-body">
                            <h4 class="card-title text-primary">@newsItem.Title</h4>
                            <p class="card-text">@newsItem.Content</p>
                            <div class="d-flex justify-content-between align-items-center">
                                <span class="text-muted">
                                    <small>Published on: @newsItem.PublishedDate.ToString("f")</small>
                                </span>
                                <a href="#" class="btn btn-outline-primary btn-sm">Read more</a>
                            </div>
                        </div>
                    </div>
                }
            </cache>
        </div>
    </div>
</div>

When you visit the /News/LatestNews URL, the page will display the latest news fetched by the NewsService. The news section’s HTML will be cached for 5 minutes, meaning the server will serve the same content for any requests within the next 5 minutes. This approach helps reduce server load while still providing updated news content within a reasonable timeframe.

In the next article, I will discuss HTML Helpers vs. Tag Helpers in ASP.NET Core MVC. In this article, I try to explain Cache Tag Helper in ASP.NET Core MVC Application with examples. I hope you enjoy this Cache Tag Helper in ASP.NET Core article.

Leave a Reply

Your email address will not be published. Required fields are marked *