Sitecore ships with some very effective cache mechanisms. One of these mechanisms is the html cache where the output of a rendered control is cached in a table which has a key for each entry. The generated key can vary by different parameters which can be set on the sublayout, xslt, view rendering etc.
The vary by possibilities are inherited from the system base template called caching.
This is usually more than sufficient control of the cache but sometimes you can also have other context depending variables which you want the cache to differ by.
To do this there are two approaches, the first one is simply to add custom parameters to the control in code depending on your context variable. This can be a fine approach if it is only one or a couple of controls which you need to control the caching of.
The other approach which in the long run seems more flexible is to override the generation of the cache key so it also reflects your context variable.
Cachekeys are generated differently for the “classic” ASP.NET webforms controls and MVC View Renderings so here I will show how it is done for both cases.
Overriding the Sitecore cachekeys in ASP.NET WebForms
First you need to derive a class from the control type on which you want to override the GetCacheKey method. In this example I am using Sublayout .
public class ContextCacheableSublayout : Sublayout
{
public override string GetCacheKey()
{
var cacheKey = base.GetCacheKey();
cacheKey = string.Concat(cacheKey, GetCustomCacheKeyPart());
return cacheKey;
}
private string GetCustomCacheKeyPart()
{
return SomeCriteria ? "_#mycustomcachekey" : string.Empty;
}
}
Then you will need to override the RenderingType which Sitecore uses for creating the webcontrols to instead instantiate your derived class.
The RenderingType classes acts like factories even though the name does not imply this.
Here I am overriding the SublayoutRenderingType
public class ContextCacheableSublayoutRenderingType : SublayoutRenderingType
{
public override Control GetControl(NameValueCollection parameters, bool assert)
{
var sublayout = new ContextCacheableSublayout();
foreach (string name in parameters.Keys)
{
var str = parameters[name];
ReflectionUtil.SetProperty(sublayout, name, str);
}
return sublayout;
}
}
Finally you need to update the web.config configuration/sitecore/renderingControls to use your new rendering type.
<renderingControls>
<control template="method rendering" type="Sitecore.Web.UI.WebControls.Method, Sitecore.Kernel" propertyMap="AssemblyName=assembly, ClassName=class, MethodName=method" />
<control template="sublayout" type="[NAMESPACE].ContextCacheableSublayoutRenderingType, [ASSEMBLY]" propertyMap="Path=path" />
<control template="url rendering" type="Sitecore.Web.UI.WebControls.WebPage, Sitecore.Kernel" propertyMap="Url=url" />
<control template="xsl rendering" type="Sitecore.Web.UI.XslControlRenderingType, Sitecore.Kernel" propertyMap="Path=path" />
<control template="webcontrol" type="Sitecore.Web.UI.WebControlRenderingType, Sitecore.Kernel" propertyMap="assembly=assembly, namespace=namespace, class=tag, properties=parameters" />
<control template="xmlcontrol" type="Sitecore.Web.UI.XmlControlRenderingType, Sitecore.Kernel" propertyMap="controlName=control name, properties=parameters" />
</renderingControls>
Overriding Sitecore Cachekeys using Sitecore MVC
When using MVC it got even easier to override the cache key.
The cachekey is generated by a processor in the renderRendering pipeline so all you need to do is to override the Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey processor.
public class GenerateCustomCacheKey : GenerateCacheKey
{
protected override string GenerateKey(Rendering rendering, RenderRenderingArgs args)
{
var cacheKey = base.GenerateKey(rendering, args);
cacheKey = string.Concat(cacheKey, GetCustomCacheKeyPart());
return cacheKey;
}
private string GetCustomCacheKeyPart()
{
return SomeCriteria ? "_#mycustomcachekey" : string.Empty;
}
}
And then patch it into the Sitecore.mvc.config instead of the GenerateCacheKey processor:
<mvc.renderRendering>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.InitializeProfiling, Sitecore.Mvc"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.SetCacheability, Sitecore.Mvc"/>
<processor type="[NAMESPACE].GenerateCustomCacheKey , [ASSEMBLY]"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.RenderFromCache, Sitecore.Mvc"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.StartRecordingOutput, Sitecore.Mvc"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.EnterRenderingContext, Sitecore.Mvc"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer, Sitecore.Mvc"/>
<processor type="Sitecore.Mvc.Pipelines.Response.RenderRendering.AddRecordedHtmlToCache, Sitecore.Mvc"/>
</mvc.renderRendering>
Another approach could be simply adding a processor after the GenerateCacheKey processor in the renderRendering pipeline and then modify the args.CacheKey as desired.
Modifying the cache in Sitecore
As I have shown in this post it is really simple to modify the cache key identifiers which Sitecore uses as keys for the html cache.
It is a shame though that it is not possible to add new cache vary by variations. These are all “hardcoded” on the system template called Caching.
Of course you could create your own checkbox “Vary by my domain specific entity” and let the system controls inherit that as well. This is just not nice to do and it risks being overwritten when upgrading Sitecore.
So Sitecore, if you want to make caching control even more flexible then make the Vary by fields dynamic and create a separate pipeline responsible for generating the cache key for both webforms and MVC.
Comments
One response to “Modifying cache keys in Sitecore”
Great article!
I knew how to do this on MVC, but was looking for the ASP .NET version.
If caching is not enabled on a sublayout the call to base.getCacheKey() will return an empty string for that sublayout. Modifying the cachekey in that case prevents that sublayout from being rendered, thus the key should be returned unmodified…