Site Meter
 
 

Monthly Archives: October 2008

DDD7 talk: “ASP.NET MVC – Show me the code!”

Just a quick announcement for UK readers: if you’re going to DDD7, you might be interested in the following session I’m presenting.

What: ASP.NET MVC – Show me the code! (60 minutes)
Where/when: DDD7 at Microsoft’s Reading campus, Saturday 22nd November 2008, 10.40am in “Chicago 1″ room
Description: Microsoft’s new MVC-based web development framework has got the bloggers all excited – but what’s it like to use? What’s wrong with classic ASP.NET, and how does MVC do better or worse? In this session we build a small web application, comparing the development experience with classic ASP.NET. See MVC architecture, clean URLs, unit testing, tight HTML, and simple ajax at work.

DeveloperDeveloperDeveloper! Day is a free one-day event run by the UK .NET community for the UK .NET community. It’s among the largest such events in the UK, so I’m excited to be presenting a session! Judging by the list of speakers and the event’s reputation, quality should be pretty high, and the technical content should be pretty deep.

Registration has just opened, but be warned: it will likely sell out within the next 24 hours…

Partial Output Caching in ASP.NET MVC

The ASP.NET platform provides two major caching facilities:

  • Data caching, which lets you cache arbitrary .NET objects in an HttpContext’s Cache collection. You can specify expiration rules and cache entry priorities.
  • Output caching, which tells pages and user controls to cache their own rendered output and re-use it on subsequent requests. This is designed to sit neatly in WebForms’ system of server control hierarchies.

Data caching continues to work perfectly well in ASP.NET MVC, because it’s just about getting objects in and out of a collection, and isn’t specific to any particular UI technology.

If only output caching was so simple! ASP.NET’s output caching facility is deeply stuck in WebForms thinking, which makes it problematic in ASP.NET MVC. You could try to use ASP.NET output caching with ASP.NET MVC, but then you’d have the following issues:

  • No usable support for partial caching. ASP.NET output caching can cache complete responses or individual server controls, but hang on: we’re not using server controls in ASP.NET MVC, so all that’s left is complete response caching.
  • Bypasses authorization and other filters. ASP.NET output caching runs very early in the request-processing pipeline (see HttpApplication’s ResolveRequestCache event), long before MVC comes in with its controllers, actions, and filters. It can’t behave like an action filter is supposed to. See update below.

Unfortunately, the [OutputCache] filter that ships with ASP.NET MVC is merely a thin wrapper around ASP.NET output caching, so it has exactly those problems. The MVC team have explained that they’re aware of the issues, but it’s very difficult to make ASP.NET output caching fit into MVC’s design, and they are focusing on other things first. And personally I’m happy with that: I’d rather see a finished 1.0 RTM release this year than fuss about output caching.

Update: Since the Beta release, the [Authorize] filter now does some clever trickery to co-operate with ASP.NET output caching. Specifically, it registers a delegate using HttpCachePolicy.AddValidationCallback(), so that it can intercept future cache hits and tell ASP.NET output caching not to use the cache when [Authorize] would reject the request. This solves the problem of ASP.NET output caching bypassing the [Authorize] filter. If you’re going to write your own authorization filter, be sure to derive it from AuthorizeAttribute so you can inherit this useful behaviour.

Note that this doesn’t stop ASP.NET output caching from bypassing any of your other action filters, and it doesn’t add any support for partial caching. If that’s a problem for you then consider using [ActionOutputCache] (below) instead.

Fixing it ourselves

One reason why I don’t mind [OutputCache]‘s limitations so much is that ASP.NET MVC is extremely extensible, and without too much trouble we can replace the output caching system with something new and more suitable.

We can quite easily create a new caching filter that captures actions’ output and uses ASP.NET’s data caching facility to store it for next time. This filter will fit properly into the MVC pipeline, not strangely bypassing authorization or other earlier filters (it will run at the right time in whatever ordered set of filters you’ve using). And if you’re using something like the PartialRequest system for widgets that I described yesterday, it will naturally let you cache PartialRequests’ output separately from the actions that host them, which is also known as partial output caching.

I must first acknowledge that the following code is very similar to the custom output caching filter that Maarten Balliauw presented way back in June. The reason I think this warrants a whole new post is because the following code works better for partial caching with PartialRequest and with MVC Contrib’s subcontrollers, and because it would be good for more MVC developers to discover it. But Maarten was first to write a good blog post on this subject.

So here it is. Drop the following class somewhere in your MVC project:

public class ActionOutputCacheAttribute : ActionFilterAttribute
{
    // This hack is optional; I'll explain it later in the blog post
    private static MethodInfo _switchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
 
    public ActionOutputCacheAttribute(int cacheDuration)
    {
        _cacheDuration = cacheDuration;
    }
 
    private int _cacheDuration;
    private TextWriter _originalWriter;
    private string _cacheKey;
 
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        _cacheKey = ComputeCacheKey(filterContext);
        string cachedOutput = (string)filterContext.HttpContext.Cache[_cacheKey];
        if (cachedOutput != null)
            filterContext.Result = new ContentResult { Content = cachedOutput };
        else
            _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { new HtmlTextWriter(new StringWriter()) });
    }
 
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        if (_originalWriter != null) // Must complete the caching
        {
            HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { _originalWriter });
            string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString();
            filterContext.HttpContext.Response.Write(textWritten);
 
            filterContext.HttpContext.Cache.Add(_cacheKey, textWritten, null, DateTime.Now.AddSeconds(_cacheDuration), Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
        }
    }
 
    private string ComputeCacheKey(ActionExecutingContext filterContext)
    {
        var keyBuilder = new StringBuilder();
        foreach (var pair in filterContext.RouteData.Values)
            keyBuilder.AppendFormat("rd{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode());
        foreach (var pair in filterContext.ActionParameters)
            keyBuilder.AppendFormat("ap{0}_{1}_", pair.Key.GetHashCode(), pair.Value.GetHashCode());
        return keyBuilder.ToString();
    }
}

Now you can use [ActionOutputCache] instead of MVC’s built-in [OutputCache]. The advantage of [ActionOutputCache] is that it’s a fully native MVC action filter, and doesn’t rely on or inherit the problems of ASP.NET’s WebForms-oriented output caching technology. So, for example, [ActionOutputCache] plays nicely with MVC’s [Authorize] filter. Update: Since the beta release, the built in [Authorize] and [OutputCache] filters now play nicely together too.

You can use [ActionOutputCache] to cache the output of any action method just like [OutputCache], but it’s perhaps most interesting when you combine it with the PartialRequests method of rendering widgets (or use it with Html.RenderAction(), which isn’t compatible with the built-in [OutputCache]). Put an [ActionOutputCache] attribute on the widget’s action method (not on the action that hosts it), then you’ll have partial page caching, as shown in the following code.

public class BlogController : Controller
{
    [ActionOutputCache(60)] // Caches for 60 seconds
    public ActionResult LatestPosts()
    {
        ViewData["currentTime"] = DateTime.Now;
        ViewData["posts"] = new[] {
            "Here's a post",
            "Here's another post. Marvellous.",
            "Programmer escapes from custody"
        };
        return View();
    }
}

image 

This is great if your widget displays some relatively static data (e.g., a list of the “most recent” things), or is an action method whose output is constant for a given set of parameters (e.g, a dynamically-built navigation menu that highlights the visitor’s current location, where the current location is one of the parameters passed to the action method), and you don’t want to recompute it on every page hit.

Support for partial caching is a major advantage of PartialRequest over the use of viewdata-populating filters and partial views to render widgets. The filter/partialview technique can never support proper output caching, because inherently it mixes the widget’s viewdata with the main page’s viewdata, and the two can’t be distinguished by the time you’re actually rendering the view. The closest you could get would be to limit yourself to data caching, but that’s more complex and not always viable anyway, such as if you’re using an IQueryable to defer a SQL query until view rendering time.

Notes

To keep the [ActionOutputCache] code short and easy to understand, and because its current behaviour is adequate for my own current project’s needs, there are a number of limitations and caveats you should know about:

  • It uses reflection to access HttpResponse’s private SwitchWriter() method. That’s how it’s able to intercept all the output piped to Response during subsequent filters and the action method being cached. It’s unfortunate that SwitchWriter() is marked private, but it is. If you don’t want to bypass the “private” access modifier this way, or if you can’t (e.g., because you’re not hosting in full-trust mode), then you can download an alternative implementation that uses a filter to capture output instead. This isn’t quite as straightforward, but some people will prefer/need it.
  • It’s hard-coded to generate cache keys that vary by all incoming action method parameters and route values, and not by anything else. You would have to modify the code if you needed the ability to vary cache entry by other context parameters (such as unrelated querystring or form values).
  • When generating cache keys, it assumes that the action method parameter types and route value types all have sensible implementations of GetHashCode(). This is fine for primitive types (strings, ints, etc.), but if you try to use it with a custom parameter types that have no proper implementation of GetHashCode(), it will pick a different cache key every time and appear not to be caching. So implement GetHashCode() properly on any such custom parameter types.
  • It doesn’t attempt to cache and replay HTTP headers, so it’s not suitable for caching action methods that issue redirections.

In other words, it works great for most straightforward widget output caching scenarios, but if you’re doing something more complex then please be prepared to dive into the code yourself! Hope this is useful to a few people.

Partial Requests in ASP.NET MVC

In your ASP.NET MVC application, it can be tricky to combine multiple independent “widgets” on the same page. That’s because a WebForms-style hierarchy of independent controls clashes awkwardly against a purist’s one-way MVC pipeline. Widgets? I’m taking about that drill-down navigation widget you want in your sidebar, or the “most recent forum posts” widget you’d put in the page footer. Things that need to fetch their own data independently of the page that hosts them.

So, you’ve got basically two options:

  1. Make sure each action prepares an absolutely complete set of ViewData for not just the main page you’re rendering but also for every widget it hosts. Then in your view you can render widgets by calling <% Html.RenderPartial(…) %>, passing to the partial view template the subset of ViewData needed to render that widget.
  2. Allow actions or views to invoke other actions, spinning off multiple internal mini-MVC pipelines that prepare and render each widget.

Incidentally, this is exactly the same choice on offer to Ruby on Rails developers, who face the exact same issue. However, in Rails-world, option 2 is frowned upon because of the framework’s performance issues. But we don’t need to have that problem in ASP.NET MVC.

These two options are both perfectly usable in ASP.NET MVC, and each has its strengths and is suited to different circumstances.

Option 1 keeps your MVC pipeline simple and elegant, but it struggles to scale up in complexity if you have many widgets or hierarchies of widgets that appear or disappear at different times. It’s nice to use filter attributes to inject the ViewData elements needed for each widget, but not nice if there are many.

Option 2 is conceptually much simpler and enables simpler code, though at runtime there are more moving parts. It’s more like having a collection of genuinely independent widgets.  This is what you get with <%= Html.RenderAction(…) %> (which is sadly relegated to the MVC Futures assembly and has been left with some technical problems), and also with MvcContrib’s new idea of subcontrollers.

You’re free to choose the option that works best for you in any individual case. If someone tells you that internal subrequests (option 2) are bad because it “isn’t MVC”, then just bite them on the face immediately. Also ask them why they’re still willing to use Ajax, and even <IMG> tags for that matter, given that both are a form of subrequest.

About subcontrollers

Firstly, thankyou to the MVC Contrib guys, because the subcontrollers idea is neat and genuinely improves on what we had before with Html.RenderAction().

The core idea of subcontrollers is putting into ViewData a delegate for each widget. The view can render the widget by invoking the delegate. This allows the view to be totally ignorant of the widget it’s rendering, leaving the controller in full control. That eliminates the main problem people had with Html.RenderAction(). Brilliant!

What I don’t like so much about MVC Contrib’s subcontrollers is that it’s quite a heavyweight and complex solution. Firstly you have to be using MVC Contrib, and then you have to learn a non-obvious set of new conventions, and an alternative controller base class, and do something funny with your default model binder. I think it’s possible to get virtually all the same benefits (and some extra ones) with a utility class that’s just 17 lines long.

Partial Requests are easy

You’ve heard of partial views, so how about partial requests? Within any MVC request, you can set up a collection of internal partial requests, each of which can set up its own internal partial requests and so on. Each partial request renders a plain old action method in any of your plain regular controllers, and each can produce an independent widget. I’m calling them partial “requests” rather than “controllers” because they run a proper MVC request-handling pipeline that’s compatible with your routing system and your controller factory. Still, as with subcontrollers, all the control remains in controllers, and the view can be ignorant.

Drop this class somewhere in your MVC project:

public class PartialRequest
{
    public RouteValueDictionary RouteValues { get; private set; }
 
    public PartialRequest(object routeValues) {
        RouteValues = new RouteValueDictionary(routeValues);
    }
 
    public void Invoke(ControllerContext context)
    {
        RouteData rd = new RouteData(context.RouteData.Route, context.RouteData.RouteHandler);
        foreach (var pair in RouteValues)
            rd.Values.Add(pair.Key, pair.Value);
        IHttpHandler handler = new MvcHandler(new RequestContext(context.HttpContext, rd));
        handler.ProcessRequest(System.Web.HttpContext.Current);
    }
}

Now, when you want to attach a widget to your output, you can put a partial request into view data as so:

ViewData["latestPosts"] = new PartialRequest(new {
    controller = "Blog",
    action = "LatestPosts"
});

… then wherever you want to display that widget in your view, put:

<% ((PartialRequest)ViewData["partialAction"]).Invoke(ViewContext); %>

… or if you prefer, use this trivial Html.RenderPartialRequest() helper:

public static class PartialRequestsExtensions
{
    public static void RenderPartialRequest(this HtmlHelper html, string viewDataKey)
    {
        PartialRequest partial = html.ViewContext.ViewData.Eval(viewDataKey) as PartialRequest;
        if (partial != null)
            partial.Invoke(html.ViewContext);
    }
}

Now, having imported the relevant namespace, your view can simply contain:

<% Html.RenderPartialRequest("latestPosts"); %>

Assuming you have a regular BlogController with an action called LatestPosts, which might render its own view (have it render an MVC View User Control rather than an entire MVC View Page) or might simply return a ContentResult, you’ll find that its output is injected at the appropriate point in your view.

image

Of course, this works seamlessly with whatever arrangement of controller factories, model binders, and action invokers you might be using, and it executes any filters that surround the partial request’s target action method.

It’s testable, too: your unit tests can pick the PartialRequest object out of ViewData, and inspect its RouteValues collection, so check it’s invoking the expected target.

It naturally supports hierarchies of widgets, too: the action that a PartialRequest calls can fill its own independent ViewData collection with other PartialRequest objects, and invoke them from its own view, and so on.

It works well with Ajax, too: since your widget is the output of a plain old action method, you could use an Ajax request to re-fetch the widget’s contents and update it in the DOM without a full page refresh.

There’s one other major benefit, too, but I’m going to save that until tomorrow, because it’s cool enough to warrant a follow-up post in its own right…

kick it on DotNetKicks.com