Kentico π Caching
Data-driven Content Management Systems, like Kentico CMS, rely heavily on caching to enable flexible content composition while still delivering information as quickly as possible to site visitors.
The Portal Engine technology, which has driven the majority of Kentico applications prior to Kentico 12, provided caching configuration out-of-the-box and allowed for highly configurable caching at the data, component,file, and page levels.
Now, developers carry more of the responsibility for caching the data they use to run their Kentico 12 MVC applications πͺ.
Fortunately the APIs for caching in custom code, provided by Kentico, are both powerful and relatively simple to use.
The Importance of Cache Dependency Keys
In a previous post (linked below), I wrote about how we can cache the results of our database queries using software design patterns like Aspect Oriented Programming, Dependency Injection, and the Single Responsibility Principle π€.
In that post I also detailed how specifying the correct cache dependency keys, for the data being cached, was important for guaranteeing the cache's Validity.
When content is updated in the CMS, we expect the caches of the (now out of date) data will be evicted from the cache π’.
We take the risk of this not happening if we don't specify the correct cache dependency keys when we store something in the cache π¨.
Cache dependency keys are effectively the glue between specific content or information in the CMS and the data cached in our MVC application.
Building Cache Dependency Keys Can Be Tricky
So, now you might be thinking to yourself, "This seems pretty important, how do we ensure we are creating the correct keys π€·ββοΈ?"
Well, Kentico does provide a nice table of cache key patterns for all the various types of data we might cache (pages, attachments, media files) π.
These patterns are strings with runtime data from our application substituted in for specific placeholders:
string key = "node|<site name>|<alias path>|<culture>";
When the tokens (like <site name>
) are replaced with real site values, we end up with a key like the following:
string key = "node|corporatesite|/home|en-us";
We can also use C# string interpolation to help us substitute in variables instead of using string concatenation π.
string siteName = "Sandbox";
string nodeAliasPath = "/home";
string culture = "en-us";
string key = $"node|{siteName}|{nodeAliasPath}|{culture}";
In my opinion they can be kinda hard to read π§ and very easy to typo β¨... (don't forget all those pipes |
!)
Another developer on our team, Michael, thought it would be a good idea to come up with a way to consistently generate these keys to avoid the issues noted above π‘.
I liked the idea a lot β‘!
It could help us avoid bugs in what would inevitably be some repetitive code (lots of queries ==
lots of caching ==
lots of keys).
It could also help us when writing tests that ensure the correct cache keys are being created π.
So, I created a library and made it open source so the rest of the Kentico community could try it out and give some feedback π!
FluentCacheKeys
The library, FluentCacheKeys, is inspired π by several other projects with readable and easy to use APIs, like FluentAssertions, FluentValidation, and GuardClauses.
wiredviews / kentico-fluent-cache-keys
Utility library for generating consistent cache dependency keys for Kentico CMS applications
Kentico Fluent Cache Keys
Utility library for generating consistent cache dependency keys for Kentico Xperience applications
Packages
How to Use?
-
Install the
WiredViews.Kentico.FluentCacheKeys
NuGet package in your project:dotnet add package WiredViews.Kentico.FluentCacheKeys
Examples
Creating cache keys for pages / documents / nodes
FluentCacheKey.ForPage().WithDocumentId(5);
FluentCacheKey.ForPage().WithNodeId(4);
FluentCacheKey.ForPage().RelationshipsOfNodeId(4);
FluentCacheKey.ForPage().OfSite("Sandbox").WithAliasPath("/home");
FluentCacheKey.ForPage().OfSite("Sandbox").WithAliasPath("/home", "en-us");
FluentCacheKey.ForPages().OfSite("Sandbox").OfClassName(HomePage.CLASS_NAME);
FluentCacheKey.ForPages().OfSite("Sandbox").UnderAliasPath("/home");
FluentCacheKey.ForPagesNodeOrder().All();
Creating cache keys for CMS objects / custom module classes
The library is built on .NET Standard 2.0, so it should work with any project using .NET Framework 4.6.1 or newer and any .NET Core project.
It also has tests, of course π€, so you can see examples and ensure the code works correctly.
You can check out the source code in the repository above, so here I'd like to give some examples of how it might be used.
If we want to create a key for the page in the CMS tree found under the alias path of /Products/Coffee/Light-Roast
for the site named Sandbox
, we could use the following call to produce the correct string:
FluentCacheKey
.ForPage()
.OfSite("Sandbox")
.WithAliasPath("/Products/Coffee/Light-Roast");
The intellisense in Visual Studio will make sure that once you select your first method (in this case .ForPage()
), only methods that make sense in that context will be allowed to follow (like .OfSite(string siteName)
) π.
I also reversed some of the language of creating cache keys to make it more readable and intuitive:
FluentCacheKey
.ForPages()
.OfSite("Sandbox")
.UnderAliasPath("/Products/Coffee/Light-Roast");
Here we immediately know that we are creating a dependency on multiple documents, due to .ForPages()
, the pages need to all belong to the same site, and under the same path π€.
We can also create keys for CMS objects or custom Module class instances:
FluentCacheKey
.ForObject()
.OfClassName(UserInfo.OBJECT_TYPE)
.WithId(2);
And attachments:
FluentCacheKey
.ForAttachment()
.WithGuid(new Guid("f4f35038-0b05-4e20-ae4e-807d9632a364"));
Or media files:
FluentCacheKey
.ForMediaFile()
.WithGuid(new Guid("a6df379b-6c14-4578-8817-298bfe54a4c3"));
To Kentico's credit, there aren't an overwhelming number of variations of cache key patterns, but it can be confusing that some take siteName
while others don't. Do we remember which ones take a Document Id compared to a Node Id π€―.
For these reasons, I think an API like this, that forces the correct values to be supplied, could be helpful.
I also like that the raw cache key strings aren't visible here, with all their pipes and specific order of tokens.
Feel free to install the NuGet package:
$ dotnet add package WiredViews.Kentico.FluentCacheKeys
Or just copy the code into your own project and make some changes if you'd like.
Summary
I hope this package either helps you organize and clarify your cache management code, or inspires you to come up with your own technique π.
I've been thinking about writing a tool to help build consistent cache item name parts, but I haven't solidified on anything I really like yet - there's too many variations and use-cases!
Let me know what you think or if you have any ideas for some utilities that could help in our Kentico projects π.
Thanks for reading π!
If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:
Or my Kentico blog series:
Top comments (1)
Very cool - thanks for sharing this Sean!