Skip navigation

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

Although design patterns are known for reducing an entire algorithm to a single word or sentence, that doesn't always happen around the Model-View-Controller (MVC) pattern. In general, the core idea of design patterns is that when someone says, "I use the Singleton pattern," everybody gets the point and understands the solution adopted without further explanation. ASP.NET MVC, instead, has quite a blurred definition for being a design pattern. Like it or not, you can find multiple definitions of MVC that share the same basic idea with some significant variations. To make an otherwise long story short, in the original formulation of the MVC pattern (from the 1980s), the view and the model were in touch through an event-based mechanism. In the modern definition of MVC (the definition we find implemented in the ASP.NET MVC framework), model and view are neatly separated, 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 of the underlying framework. So in ASP.NET (and web in general), two successive requests are completely independent from one another, and their output must be regenerated from scratch. In ASP.NET Web Forms, the viewstate and the overall page lifecycle simplified much of this work, allowing developers to 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 developers to 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 a view 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 decides the action to take. The action produces data for the view. Next, the controller selects the next view to render and passes data to incorporate. The basic 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 code belong 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 (as discussed in last month's installment of this column) to take any required code out of controller classes. Another option is adding ad hoc methods to existing controllers or, better yet, ad hoc controllers whose only purpose in life is serving data to the views. These controllers (or methods) are never 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 to incorporate external markup. This is markup that results from the application of processing logic-logic that you don't want to stay in the view. The syntax 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 if it returns an HTML markup string. In addition, a render action can be decorated with the ChildActionOnly attribute, which has the effect of hiding the method 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 with an 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 a number of actions in relation to your application. Whatever the interaction and subsequent output, the menu has to be rendered. This means that the controller 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 clearly too much of a responsibility for a NewsController class, isn't it? Rendering the menu, therefore, is not an action directly related to the current request. 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 render action is typically generated by a call to PartialView, which selects a child view optionally with its own view model. The invoking view will then receive 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 or another 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 helper methods for this purpose: Html.Action and Html.RenderAction. The two helpers work mostly in the same way with just one subtle difference. The method Html.Action returns the markup as a string, whereas RenderAction writes directly to the output stream. The following example shows how to use Html.Action:

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 shows a possible concrete implementation for the method that merely wraps up the default menu of the standard ASP.NET MVC 3 application created by Visual Studio tooling. Using RenderAction requires a little bit of extra attention. If you simply replace Action with RenderAction in the previous code snippet, all you get is a compiler error. Here's the correct way to use RenderAction:

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 add to 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 anonymous object (items in the example) must match the name of the formal parameter defined on the method. As mentioned, binding rules are honored, and without matching names that would be impossible.

Figure 2: Passing data to a render method

 


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 method via reflection. In particular, a render action is processed as a child request within the boundaries of the main user request. The RenderAction method builds 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 is then 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 other hand, 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 attribute won'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 barriers that prevent render actions from returning strings or objects. The point is that the Action and RenderAction helpers you use to invoke such actions expect 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 child action-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 of time. Interestingly, by using OutputCache with a child action, you can cache only a portion of the view, thus achieving partial page caching as in classic ASP.NET Web Forms.

Figure 3: Achieving partial caching through render actions
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 render action 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 loudly demanded. Even so, render actions also have generated negative feelings in another part of the community. Why? Render actions are methods that the view calls back in the controller. A strong design point about MVC is the neat separation of controller and view. In this regard, render actions just break this separation. Render actions are effective to use; balancing design with effective solutions is the developer's job.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish