This is the fourth post in a series of posts covering Episerver output caching; I would recommend reading Episerver Caching - Output Caching Strategies Explained.... What Is a Donut Cache? to understand some of the needs as to why you might want to create a custom output cache provider.
In the previous articles, I've covered how to enable the output cache on the pages and blocks using a full page caching strategy, as well as setting custom cache keys/params to deal with custom content that might be causing the cache engine to return the wrong HTML, in certain circumstances. If you have decided, however, that a 'full page' caching strategy isn't the right approach for your project, then the next part of the puzzle that you will have to deal with, is how to implement donut caching in Episerver.
What makes donut caching more complex and confusing to implement than full page caching is because.NET manages full-page output cache and partial page caching differently. In a 'full page' cache scenario, the traditional runtime output cache attribute is used. In 'partial page' caching, .NET uses a key/value pair strategy using the MemoryCache-object so it's not as easy as just enabling the [OutputCacheAttribute] on your blocks.
To further complicate the matter in Episerver caching, it is easy to get confused between block caching and partial block caching. Under the hood. NET will use 'partial' caching when a block doesn't have its own controller. In today's guide, we're going to cover the basics of setting up a custom cache provider. In theory, there are two ways to implement this, although in practice I've only found one works a lot better.
Creating A Custom Cache Provider
In a normal MVC website, a common scenario when you need a more complex caching approach, like a donut cache, is to create your own caching provider. In your code, you inherit from 'OutputCacheProvider' and implement the overrides and you should end up with a skeleton class that looks like this:
In your caching section in your web.config you set your default provider:
If you try and implement this out, as is, you'll find two issues. One, your Get and Set will never be hit by the debugger in Visual Studio (it didn't for me at least!!!).
When you try and load your website you may see: 'When using a custom output cache provider like 'EPiServerOutputCacheProvider', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.'
As most of the documentation I've found on the web doesn't clearly cover how to fix these two issues in MVC, I'll cover the basics. In Episerver 8 (I think) a new way interface was introduced to enable us to cache. The IObjectInstanceCache, (see Object caching).
With this interface and a little bit of configuration, we can still create intercept all the requests to the cache without having to implement a custom OutputCacheProvider.
This benefits us in two ways.. the debugger will now work and we can do all our configuration in code, rather than the web.config. The skeleton code for the custom provider now looks like this:
In the web.config we don't need any code to set the custom provider. I create an Initialization module.
if you tried to run this code, you should see the Get and Add being hit in your debugger. The object cacher works for CMS and commerce.
The next issue we will encounter is the OutputCacheProvider not supported error. This one is more dependent on your implementation. The reason for this error is that you need to set specific Episerver cache dependencies when you create a custom implementation.
If you haven't set these cache item dependencies, the site will fail. If you ever tried to implement a custom output cache provider in a webforms solution and wanted to create custom cache provider policies, we would override the SetCachePolicy() method on the Base Page to set these missing properties. When we work with MVC we don't have a concept of a base page.
If you want to implement a more custom per-page caching policy, you can create a custom OutputCacheAttribute and add any custom logic that you want to in there. This means that if you want to cache some content, you will need to create a custom OutputCacheAttribute to decorate your pages/blocks to set these essential cache items. The code below that should stop your website was mainly ripped from another blog, but the code should look similar to this:
to create a custom output cache provider to implement something like donut caching within Episerver, you generally have to jump through a few more hoops than you would in a standard MVC build. When implementing a custom output cache provider, Episerver recommends using Object Caching and the IObjectInstanceCache interface.
Doing this will also make your debugging issue a lot easier. As EpiServer is expecting certain dependencies to be set to perform it's caching, you won't be able to use the standard OutputCache attribute in your project.
Instead, you will need to create a custom attribute that sets these dependencies whenever you want to cache. If you don't, Episerver will throw a server error.