In this tutorial, you will learn how about a donut caching I created that allows you to use donut caching within Episerver.
This is the fifth post in my series covering output caching in Episerver. As the number of posts implies, caching in Episerver can be complicated. If you ar enwe to caching I recommend reading, 'output caching explained' and what is a donut cache.
Installing the Donut Cache
I have created a donut hole project you can use to implement donut hole caching, but if I needed to write everything from scratch, it would have taken me far too long. Instead, I cheated somewhat by using an existing donut caching Nuget package to get us 80% there.
DevTrends have created a really good open source donut caching package that is a great starting place to create an Episerver version from. The first thing we need to do is add the Donut cache framework into your solution.
Open up Nuget type 'donut' and you should see the DevTrends MvcDonutCaching package.
Understanding The Donut Cache
One of the big limitations of the dev trends version in terms of implementing it within Episerver, is that it was based on MVC routing. The dev trends version comes with an HTML partial helper that will create donut holes based on controllers and actions. The dev trends version then creates unique cache keys based on the controller/action combo.
To get donut caching to work in Episerver we need an HTML helper that creates holes based on IContent and ContentAreas and have the unique cache keys based on serialized content references.
Luckily, for you, I have done just that and the code is available from Nuget from here.
How It Works
The EpiserverDonutCache comes with 'EpiserverDonutCacheAttribute' this is the attribute you will need to decorate your controllers with to process donut holes.
Donut holes are created in views by using the provided HTML helpers. The helpers work with ContentAreas or IContents so you have some flexibility. When the page is being processed and a hole is created, a donut comment is added to the HTML. The HTML comment has a content reference associated with it. After the page is rendered, this version with donut comments is added to the output cache.
The next time a page request is made, the HTML that has been cached against the page (including the donut comments) is retrieved from the donut cache and then a replacement is done via a regular expression on all the donut comments on the page.
This replace directly calls the page, or, block controller for the item being donut cached. The benefit of directly calling page and block controllers means we can add an additional level of caching in pages. Depending on how you set up your controllers, these holes can either be additionally cached with their own expiry time, or, we can configure the controllers to just get the HTML for the block live each time.
I admit this can be hard to get your head around, so if you are struggling with this a bit, I would recommend checking out my Github example, if you're getting confused. By having this level of caching configuration is quite powerful. For example. you may cache more out the page for 24 hours for example as the content doesn't change very often, but, have the donut holes only cache for a maximum of 5 minutes.
By having this different cache times should reduce a lot of load off of your server as the CPU only has to re-build small parts of your page every now and again rather than the whole page every 5 minutes.
Pages On all your top level pages you need to add the EpiserverDonutCacheAttribute and the duration you want to cache the page (the duration is measured in seconds)> My start page would look like this:
Blocks and Partials
To make a block or partial work with the donut cache, you will need to decorate it with the 'ChildActionOnly' attribute. If you forget to add this, then the donut whole will not render correctly.
If you want the block to be rendered each time then this is all you need to do. If you want to cache your block for a period of time, you need to use the EpiserverDonutCache attribute again. To cache a block for 60 seconds you would decorate it like this:
Holes Within Holes
In some instances, you may only want to create a donut hole within a child action. Say you have a carousel, you may want the carousel slides to be donut cached. If you want to have nested donut within your code, you need to use the OutputCacheOptions.ReplaceDonutsInChildActions attribute. Without this, the donut holes will not work correctly.
Now you know how to configure the controllers so MVC can correctly parse the donuts, we should probably create a few to test it out. In your view, you can either create a hole around a single IContent or a content area. The IContent is probably easier so we'll cover that first.
Like normal content areas, I have also created an overload that will take the standard additional parameters, like so:
To hopefully make your life a little easier, I have created a fully working demo website with everything you need to get you up and running.
You can download this sample site from my Github account here: JonDJones.Com.EpiserverDonutCaching
So, there you have my Episerver donut caching package, hopefully, explained, so you can get going with donut caching in your project. The key to implementing donut caching successfully is decorating your controllers correctly. On all your top-level pages you need to use the 'EpiserverDonutCache' attribute.
Within each child block controller, you need to use the 'ChildActionOnly'. If you want to create nested donuts you need to use the 'OutputCacheOptions.ReplaceDonutsInChildActions' option. The duration is measured in seconds (not minutes) so be aware!