How to Use the ASP.NET MVC Render Action Helpers
When populating an ASP.NET MVC view requires some action, render actions come to the rescue
November 23, 2011
Although design patterns are known for reducing an entire algorithm to a single word or sentence, that doesn't always happen around theModel-View-Controller (MVC) pattern. In general, the core idea of design patterns is that when someone says, "I use the Singleton pattern," everybodygets the point and understands the solution adopted without further explanation. ASP.NET MVC, instead, has quite a blurred definition for being adesign pattern. Like it or not, you can find multiple definitions of MVC that share the same basic idea with some significant variations. To make anotherwise long story short, in the original formulation of the MVC pattern (from the 1980s), the view and the model were in touch through anevent-based mechanism. In the modern definition of MVC (the definition we find implemented in the ASP.NET MVC framework), model and view are neatlyseparated, and the view receives the data to work on (i.e., the view model) directly from its caller-the controller.
Design patterns are just high-level tools; when it comes to concrete implementation, you may be facing other issues mostly related to technicalities ofthe underlying framework. So in ASP.NET (and web in general), two successive requests are completely independent from one another, and their outputmust be regenerated from scratch. In ASP.NET Web Forms, the viewstate and the overall page lifecycle simplified much of this work, allowing developersto focus essentially on the section of the page that has to be updated in the request. As you saw in my article last month, "Filling the View Model," in ASP.NET MVC the intentional lack of viewstate and the different page architecture force developersto re-create the view entirely per each request.
Last month I presented a special action filter attribute that helps in splitting the core code of a request from the repetitive code required to fill aview model. This column is dedicated to exploring an alternative approach to solve the same problem: render actions.
Controller/View Mechanics
In ASP.NET MVC, the controller is the component that deals with the request details. The controller reads the information from the request and decidesthe action to take. The action produces data for the view. Next, the controller selects the next view to render and passes data to incorporate. Thebasic mechanics work very well as long as the view is a thin and humble object-no processing logic, just plain rendering and binding logic.
But what if adjusting the data for the view requires some work? What kind of work? Things like retrieving data from the cache or some database,filtering view data such as images or links per user or scenario, and loading preferences from cookies or persistent storage. What does this codebelong to?
One option is to move this code in the controller layer so that the view always receives ready-to-display data. You can use action filters (asdiscussed in last month's installment of this column) to take any required code out of controller classes. Another option is adding ad hoc methods toexisting controllers or, better yet, ad hoc controllers whose only purpose in life is serving data to the views. These controllers (or methods) arenever exposed as public URLs but are exclusively invoked by the view in an internal child procedure.
What Are Render Actions, Anyway?
As mentioned, render actions are special controller methods defined only to be called back from the view. Typically, the view calls render actions toincorporate external markup. This is markup that results from the application of processing logic-logic that you don't want to stay in the view. Thesyntax of a render method is nearly the same as the syntax of a regular controller method.
A render action is a public method on the controller class. You can define a render action method to return any data, but you can only safely use it ifit returns an HTML markup string. In addition, a render action can be decorated with the ChildActionOnly attribute, which has the effect of hiding themethod to public callers.
A Sample Render Action Method
The main design problem that model filler action filters and render actions try to solve is keeping the controller code highly focused. Let's go withan illustrative example.
Suppose you have a global menu to render in a number of views. Each view may have a number of ways to interact with the user, and the user may take anumber of actions in relation to your application. Whatever the interaction and subsequent output, the menu has to be rendered. This means that thecontroller method that, say, uploads a news item must be informed about the details of the menu and where the items are to be retrieved. It's clearlytoo much of a responsibility for a NewsController class, isn't it? Rendering the menu, therefore, is not an action directly related to the currentrequest. Yet rendering the menu is necessary, and render actions are a valid approach to solve the issue. Figure 1 shows a sample render action.
Figure 1: A sample render action
public class MenuController : Controller
{
[ChildActionOnly]
public ActionResult List()
{
var model = new MenuViewModel();
model.MenuItems.Add(new MenuItem()
{
ActionName = "index",
ControllerName = "Home",
LinkText = "Home"
});
model.MenuItems.Add(new MenuItem()
{
ActionName = "about",
ControllerName = "Home",
LinkText = "About us"
});
return PartialView("_Menu", model);
}
}
As you can see, the render method is not really different from a regular method, and it is still subject to model-binding rules. The output of a renderaction is typically generated by a call to PartialView, which selects a child view optionally with its own view model. The invoking view will thenreceive a chunk of HTML and integrates that with the rest of the view.
As an alternative method, you can write the render action to return a plain data structure whose content is determined by the controller class oranother helper class you may have. This method is also demonstrated in Figure 1.
Invoking a Render Action
Now that you know how to define a render action, the next step is learning how to invoke it. ASP.NET MVC makes available a couple of handy helpermethods for this purpose: Html.Action and Html.RenderAction. The two helpers work mostly in the same way with just one subtle difference. The methodHtml.Action returns the markup as a string, whereas RenderAction writes directly to the output stream. The following example shows how to useHtml.Action:
@Html.Action("list", "menu")
The net effect of this code is to fill the DIV element with any markup returned by the method List invoked on the Menu controller. Figure 1 only showsa possible concrete implementation for the method that merely wraps up the default menu of the standard ASP.NET MVC 3 application created by VisualStudio tooling. Using RenderAction requires a little bit of extra attention. If you simply replace Action with RenderAction in the previous codesnippet, all you get is a compiler error. Here's the correct way to use RenderAction:
@ { Html.RenderAction("list", "menu"); }
In addition, both methods support a variety of overloads through which you can specify multiple parameters, including route values, HTML attributes,and, of course, the controller's name. For example, the List method of the Menu controller (see Figure 1) is expected to know the list of items to addto the menu. Figure 2 presents a code snippet that shows how you can pass menu items as an argument. Note that the property name in the anonymousobject (items in the example) must match the name of the formal parameter defined on the method. As mentioned, binding rules are honored, andwithout matching names that would be impossible.
Figure 2: Passing data to a render method
@{Html.RenderAction("list1", "menu",
new { items = new List {
new MenuItem { ActionName="index", ControllerName="home", LinkText="Home"},
new MenuItem { ActionName="about", ControllerName="home", LinkText="About us"}
}});
}
Render Actions as Child Actions
When you call a render action method, a lot happens under the hood. In brief, the execution of a render action is not simply a call made to a methodvia reflection. In particular, a render action is processed as a child request within the boundaries of the main user request. The RenderAction methodbuilds a new request context and fills it up with the same HTTP context of the parent request and a different set of route values. The child request isthen forwarded to a specific HTTP handler-the ChildActionMvcHandler class-and executed as if it came from the browser.
The overall operation is similar to what happens when you call Server.Execute in general ASP.NET programming. There's no redirect and no round trip,but the child request goes through the usual pipeline of an ASP.NET MVC request and honors any filters that it might encounter. Filters, on the otherhand, may detect that they're called in a child action and skip any actions.
By default, any action method can be invoked from a URL and via a render action. However, any action methods marked with the ChildActionOnly attributewon't be available to public callers, and their usage is limited to rendering actions and child requests.
Non-Markup Render Actions
Render actions have been created simply to let you return some ready-made markup for the view to integrate. However, there are no technical barriersthat prevent render actions from returning strings or objects. The point is that the Action and RenderAction helpers you use to invoke such actionsexpect to receive HTML strings. The following code shows how you can return a plain string:
[ChildActionOnly]
public ActionResult AppCopyright()
{
Var text = ...;
return Content(text, "text/html");
}
You can't return anything other than strings; if you return objects, the string representation of the object will be taken into account.
Partial Caching
Up until ASP.NET MVC 3, some valuable action filters didn't honor render actions. Some of these filters check whether the current action is a childaction-they do so via a Boolean property on the controller context-and exit without doing any further work. A notable example of a filter that,starting with ASP.NET MVC 3, works well with child actions is OutputCache. This filter allows you to cache the output of a method for a given amount oftime. Interestingly, by using OutputCache with a child action, you can cache only a portion of the view, thus achieving partial page caching as inclassic ASP.NET Web Forms.
Figure 3: Achieving partial caching through render actions
Figure 3 shows a sample page where partial caching is used. As you can see, the time displayed in the lower part of the figure is due to a renderaction marked with OutputCache, as shown in the following example:
[ChildActionOnly]
[OutputCache(Duration=5)]
public ActionResult CurrentTime()
{
return PartialView();
}
Here's the associated markup:
Time: @DateTime.Now.ToString()
@Html.Action("currenttime", "menu")
As mentioned, you need ASP.NET MVC 3 to take advantage of this feature.
A Question of Balance
Although they were not included in the first releases of ASP.NET MVC, render actions are a feature that the ASP.NET developer community loudlydemanded. Even so, render actions also have generated negative feelings in another part of the community. Why? Render actions are methods that the viewcalls back in the controller. A strong design point about MVC is the neat separation of controller and view. In this regard, render actions just breakthis separation. Render actions are effective to use; balancing design with effective solutions is the developer's job.
About the Author
You May Also Like