Output Caching in MVC 3

This post is part of the series I’m doing on the newly released ASP.NET MVC 3.

MVC supports caching on 2 levels – one is the entire page (available since MVC 1), the other is portions of a page (only available in MVC 3).

The easiest way to understand how it works it with an example.

How is it used

As an example I’m going to use a very simple e-Commerce example.  I’m going to display a list of products and a user can choose to view a product.

List of Books

I used AutoPoco to generate the test data, but more on that in a later post.

As you can see each product is also assigned a category – books in this case.  When the user views a specific item, I also want to display the specials for this category.  To do this, I’m going to use a child action in the view.

@model CachedProducts.Models.Product
@{
    ViewBag.Title = Model.Name;
}

<h1>@Model.Name</h1>
<h2>@Model.FormattedPrice</h2>
<p>@Model.Description</p>

@Html.ActionLink("All Products", "Index")

@Html.Action("Specials", new { Model.Category })

This is a normal action on our controller that simply returns a partial view containing the specials.

public ActionResult Specials(ProductCategory category)
{
    var specials = specialRepository.GetSpecials().Where(s => s.Category == category).ToList();

    return PartialView(specials);
}

Let’s take a look at the result.

eCommerce Detail

Nothing special so far (no pun intended).  The related specials is a very good example of data that can be cached – the specials are unlikely to change very often, and if the data is slightly out of date it’s (hopefully) not the end of the world.

To enable caching on the specials we use the OutputCache attribute on our child action.

[OutputCache(Duration = 600, VaryByParam = "category")]
public ActionResult Specials(ProductCategory category)
{
    var specials = specialRepository.GetSpecials().Where(s => s.Category == category).ToList();

    ViewBag.CreatedOn = DateTime.Now;

    return PartialView(specials);
}

By applying this attribute I am specifying that the result should be cached for 600 seconds (10 minutes) and the output should be unique for the specified category.  I am also passing the current time to the view in order to test the caching.

eCommerce Cached Specials

It’s very easy to verify that our specials are indeed being cached, and are indeed cached by category.  Very cool.

How to do this in MVC 2

MVC 2 already supports the OutputCache attribute, but it only allows us to cache entire actions – we couldn’t cache only certain portions of a view.  There are a few developers who implemented their own solution, but apparently due to the way that MVC 2 requests, data can be cached when it shouldn’t be.  The solutions I found were by Steve Sanderson, Maarten Balliauw and Phil Haack.

MVC 3 overcomes this problem with the introduction of child actions – very easy to use while maintaining the separation of concerns within MVC.

Further Reading

Scott Guthrie did a brief explanation of partial output caching in his announcement of the MVC 3 Release Candidate.  (Just look for the section that says Partial Page Output Caching)  DotNetSlackers also did a more in-depth article on caching in MVC in general.

Happy coding.