This blog post is about how to implement ASP.NET MVC 4 output cache with EPiServer 7 Preview.
Output cache in ASP.NET MVC 4
Output cache in ASP.NET MVC is built on top of the ASP.NET framework. Though ASP.NET MVC manages full page output cache and partial page output cache differently. Partial page output cache is equivalent to traditional web form user control output cache. See my blog post on EPiServer CMS Fragment Caching for more information on the topic.
To use page output cache in ASP.NET MVC we mark an action in a controller with the OutputCache attribute. The content rendered by the action will then be added to the output cache. The same way we can add the OutputCache attribute to child actions to support fragment caching where we don’t want to cache the entire output of the page.
But from here full page output cache and partial page output cache are managed differently by the ASP.NET MVC framework. In short the full page output cache is managed by the output cache module traditionally used by web forms, and the partial page output cache is implemented with the MemoryCache-object introduced in .NET 4.
Because of how ASP.NET MVC manages the partial page output cache we cannot add EPiServer page data cache dependencies or alter the key which is used to identify and retrieve the cached content.
By default ASP.NET MVC uses the route as key to identify output cache items, this is a problem for us since by the use of friendly URLs multiple page instances can have the same route as the route is defined by the page type.
Target criteria
- The output cache item should depend on the page data instance it was generated for
- The output cache should be identified by page ID and route instead of just route
- User specific content should not be added to the output cache
- Partial page output cache should hook into the default output cache provider also used by full page output cache
ASP.NET MVC Extensible Donut Caching
ASP.NET MVC Extensible Donut Caching by DevTrends is an output cache framework for ASP.NET MVC 3 or later. Partial page output cache and full page output cache are not managed differently, instead they hook into the same default output cache provider.
Another great function of the ASP.NET MVC Extensible Donut Caching framework is that we can use full page output cache and exclude specific child actions from being added to the output cache – also known as donut caching. We do this by using the extension method to Html.Action where the first parameter is the child action to be rendered and the second parameter declares if the action should be excluded from the output cache. See sample code below.
<div class="row">
<div class="six columns">
@* This action will not be added to the cache even if we’re using full page output cache *@
@Html.Action("NoCache", true)
</div>
<div class="six columns">
@Html.Action("Cache")
</div>
</div>
There are other cool benefits of using the ASP.NET MVC Extensible Donut Caching framework such as being able to use output cache profiles for partial page output cache. Go read about them in the blog post Donut Output Caching in ASP.NET MVC 3 by DevTrends.
EPiServer 7 ASP.NET MVC 4 output cache provider
The ASP.NET MVC Extensible Donut Caching framework met the following criteria:
- User specific content should not be cached. This was available before in ASP.NET MVC, but now we can add an entire page to the output cache while also excluding child actions that contain user specific content
- Partial page output cache also hooks into the default output cache provider
This is awesome. But we still need to depend the partial page output cache to the page data instance and identify the output cache by page ID and route instead of just route. We do this by creating our own output cache provider where we can add our output cache items to the application cache along with cache dependencies and cache key. See sample code below. I’ve left out using-statements and XML comments for brevity.
namespace DBLOG.EPiServer7CTP9.Website.Cache
{
public class EPiServerOutputCacheProvider : OutputCacheProvider
{
public override object Add(string key, object entry, DateTime utcExpiry)
{
ContentReference reference = HttpContext.Current.Request.RequestContext.GetContentLink();
// Never add content to the output cache if the user is authenticated (logged in)
if (reference != null && !HttpContext.Current.User.Identity.IsAuthenticated)
{
// In addition to the key generated by the donut caching framework we add the page ID as well
string outputCacheKey = GetOutputCacheKey(key);
// Create cache dependency to the page for which we're adding output cache for
CacheDependency dependency = DataFactoryCache.CreateDependency(reference);
// Add the cached data to the application cache with the page data dependency
HttpContext.Current.Cache.Add(outputCacheKey, entry, dependency, utcExpiry, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
return entry;
}
public override object Get(string key)
{
// If the user is logged we won't show cached data for editorial purposes
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
string outputCacheKey = GetOutputCacheKey(key);
return HttpContext.Current.Cache.Get(outputCacheKey);
}
public override void Set(string key, object entry, DateTime utcExpiry)
{
string outputCacheKey = GetOutputCacheKey(key);
HttpContext.Current.Cache.Remove(outputCacheKey);
this.Add(key, entry, utcExpiry);
}
public override void Remove(string key)
{
string outputCacheKey = GetOutputCacheKey(key);
HttpContext.Current.Cache.Remove(outputCacheKey);
}
private string GetOutputCacheKey(string key)
{
ContentReference reference = HttpContext.Current.Request.RequestContext.GetContentLink();
if (reference != null)
{
return string.Format(
"EPiServerOutputCache:{0}:{1}",
reference.ID,
key);
}
return key;
}
}
}
We need to set the default provider in web.config so that the ASP.NET MVC Extensible Donut Caching framework will use our custom output cache provider instead of the default ASP.NET MVC provider. Add the following outputCache-element as a child node to the caching-element.
<outputCache defaultProvider="EPiServerOutputCacheProvider">
<providers>
<add name="EPiServerOutputCacheProvider" type="DBLOG.EPiServer7CTP9.Website.Cache.EPiServerOutputCacheProvider, DBLOG.EPiServer7CTP9.Website"/>
</providers>
</outputCache>
In action
From the sample markup earlier the output is as presented below if the user isn’t logged in to the CMS. When an editor publishes a new version of the page the output cache is ejected from the application cache since it depends on the page data instance.
Wrapping it up
The use of output cache can significantly improve a site’s performance if heavy logic is performed by one or more controllers. Where EPiServer has been selected as the CMS for heavy duty operations the proper use of output cache is a key factor in the success of the website.
Also; when implementing responsive web design performance is critical when dealing with mobile users – make that site go like greased lightning!
Thanks for reading!


Daniel lives in Helsingborg, Sweden and works at Sogeti as a solutions architect with a passion for web technologies.






