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:
- 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.
- 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 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.
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…