[Update - ok, so this officially doesn't work, which sucks. I'm having a look at alternatives, but at the moment it looks like that is going to be reverting to MVC 1.0]
To do donut caching in Asp.Net Mvc 1.0 required quite a bit of hacking (or at least that was the only way I could get it to work), but with version 2 things get much better because of the addition of the Html.Action method.
As a quick recap, ‘donut caching’ is when you cache the whole of a web page except for small parts within it – the holes in the donut. A classic use for it is pages which have some personalised aspect like a logged in user name but are otherwise identical for all users.
To solve exactly this scenario I used the following code:
A child action called LoggedInUserInfo in my HomeController
[ChildActionOnly]
public ActionResult LoggedInUserInfo(HttpContextBase context)
{
return View(context);
}
with a view of the same name:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HttpContextBase>" %>
<%@ Import Namespace="KableDirect.Service" %>
<%@ Import Namespace="KableDirect.Web.FrontEnd.Models.Shared"%>
<ul>
<li>You are logged in as <%= Model.User.Identity.Name %> </li>
<li><%=Html.ActionLink("My Profile", "Profile", "Account")%></li>
<%if (Model.User.IsAdmin()){%>
<li><a href="<%= RouteHelper.GetAdminUrl(Model)%>">Admin</a></li>
<%}%>
<li><%=Html.ActionLink("Logout", "Logoff", "Account")%></li>
</ul>
I also have the following HtmlHelper method to make calling the asp.net WriteSubstitution() method easier:
public delegate string MvcCacheCallback(HttpContextBase context);
public static class CacheHelper
{
public static object Substitute(this HtmlHelper html, MvcCacheCallback cb)
{
html.ViewContext.HttpContext.Response.WriteSubstitution(c => cb(new HttpContextWrapper(c)));
return null;
}
}
Now I can call my child action within my MasterPage using Html.Action as follows:
<%= Html.Substitute(context => Html.Action("LoggedInUserInfo", "Home", new {context}).ToString()) %>
It is important that your action takes the context as a parameter or it will only be called once the first time the page is hit and then get cached along with everything else.
It is also important that you have a route in your route table that will catch the {controller}/{action} url pattern. See this post for more details.
Phill Haack now has a warning on his site about problems with donut caching in Asp.Net Mvc 2, but this has worked fine for me, I’ll update here if I find scenarios where this doesn’t work.
There is a good introduction to Html.Action here.
Donut caching is available for ASP.NET MVC 2 and 3 in ‘Moth’ (NuGet: Install-Package Moth). See the docs at the Github wiki.
Interesting, thanks for the tip.