Caching news: The cache-dependency tag

   —   

In today’s article I would like to introduce a new tag helper added to Kentico Xperience 13 in the first Refresh. The cache-dependency tag helper enhances caching in Razor views for .NET Core MVC projects.

I also recommend reading the Caching news: Page builder widgets article and its sequel.

Caching before the Refresh

You might have used the cache element in your Razor views before. And maybe you have stumbled across a problem illustrated by the following snippet 

<cache expires-after="@TimeSpan.FromMinutes(60)"> foreach (var article in @Model.LatestArticles) { <div class="article"> ... </div> } </cache>

The enumerated articles are cached in the view for 60 minutes. However, what happens if a new article is added? Or an existing one gets changed? The visitors of your website have to wait until the cache expires.

When it comes to caching in controller actions, the situation is quite straightforward. As can be seen in the documentation, you can specify cache dependencies using a variety of dependency cache keys. With regard to our case above, we could go with something as simple as the nodes|mysite|my.article.type|all cache key.

The question is how to specify cache dependencies when using the cache element offered by the .NET Core framework for MVC?

Using the cache-dependency tag

This is where the cache-dependency tag steps in. Using its cache-keys attribute, an array of dependency cache keys can be specified

@{ var articleCacheKeys = new[] { "nodes|mysite|my.article.type|all" }; } <cache expires-after="@TimeSpan.FromMinutes(60)"> <cache-dependency cache-keys="@articleCacheKeys" /> @foreach (var article in @Model.Articles) { // ... } </cache>

Now any time a new article is added or an existing one modified, the cache invalidates.

Of course, we could leverage a different cache key, such as node|mysite|/latest/articles/section|childnodes to constrain the cache invalidation only to a particular section of our site’s articles.

In other scenarios, we could even collect the IDs of individual articles during their enumeration and then render the cache-dependency tag with dependency cache keys based on the gathered IDs. Take this simple enumeration example

<cache expires-after="@TimeSpan.FromMinutes(60)"> @foreach (var article in @Model.Articles) { // ... cacheKeys.Add($"documentid|{article.DocumentID}"); } <cache-dependency cache-keys="@cacheKeys.ToArray()" /> </cache>

The enabled attribute

Once we have caching in place, there is one subtle thing left to address. We probably do not want to cache articles when site editors are previewing them via the administration interface. To prevent that, simply use the enabled attribute

@{ var articlesCacheKeys = new[] { "node|site|my.article|all" }; var cacheEnabled = !Context.Kentico().Preview().Enabled; } <cache enabled="@cacheEnabled"> <cache-dependency cache-keys="@articlesCacheKeys" enabled="@cacheEnabled" /> // foreach articles </cache>

Note how the cacheEnabled flag is propagated to both the cache element and the cache-dependency. This is to prevent redundant processing of cache dependencies when the cache is disabled.

The internals – ICacheDependencyAdapter interface

Sometimes it is useful to know the magic behind the curtain. Should you need to implement custom caching based on the MemoryCache in .NET Core, the CMS.Helpers.Caching.ICacheDependencyAdapter comes in handy when transforming cache dependency keys to change tokens.

The GetChangeToken(string[] dependencyCacheKeys) method provides an IChangeToken that can be used when creating an entry in the memory cache

string[] cacheKeys = new string[] { /* ... */ }; IChangeToken changeToken = CacheDependencyAdapter.GetChangeToken(cacheKeys); MemoryCache.Set(key, data, changeToken);

Summary

The cache-dependency tag introduced in the first Xperience 13 Refresh allows specifying of dependency cache keys within the cache tag

@{ var cacheKeys = new[] { "nodes|mysite|my.article.type|all", "..." }; } <cache expires-after="@TimeSpan.FromMinutes(60)"> <cache-dependency cache-keys="@cacheKeys" /> // ... </cache>

Moreover, the CMS.Helpers.Caching.ICacheDependencyAdapter interface transforms dependency cache keys to change tokens, addressing further needs of custom caching in conjunction with .NET Core’s MemoryCache.

Share this article on   LinkedIn

Marek Fesar

Hi, I am the Principal Technical Leader here at Kentico and I primarily focus on the design, architecture and core of our solution.