The View Is All Yours

The Default Engine for You to Generate the View in an ASP.NET MVC Framework Scenario

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 3.5

 

The View Is All Yours

The Default Engine for You to Generate the View in an ASP.NET MVC Framework Scenario

 

By Dino Esposito

 

One of the reasons the ASP.NET MVC Framework is so popular these days (even though it still has to go beyond the stage of a preview software) is the neat separation it shows between the constituent parts of a classic Web application. At the very minimum, a Web application is made up of a collection of pages. These pages contain some HTML template that is then populated before display by the combined action of some server-side code and data retrieved from a persistence layer.

 

In five years of classic ASP.NET, we never had a neat and effective separation between the HTML template and the code-behind logic let alone a separation between the page itself and the run-time environment required to make it work. With the ASP.NET MVC Framework, Microsoft is offering an alternative model that changes the way in which developers plan for new Web pages and applications.

 

In the MVC Framework, each page is seen as a collection of server methods, each bound to a particular request that may come from the host browser. These methods are not defined in the class that represents the page itself, as was the case with the classic Web Forms model. Page action methods are defined on a separate controller class that, among other things, can be developed and tested in isolation. The controller, however, is just one gear in the engine. Another no less important gear is the view. In this article I ll focus on the internal logic that drives the view classes in the ASP.NET MVC Framework and how they actually generate the resulting markup.

 

From the Controller Down to the View

In the ASP.NET MVC Framework, any method on a controller class that needs to update a view, or generate a new view, returns an instance of the ViewResult class. Let s consider the world s simplest controller action, right from the sample site that Visual Studio 2008 sets up when you create a new MVC project:

 

public ActionResult About()

{

   // Your action takes place here

   ViewData["Title"] = "About Page";

   // Creation of the view is ordered here

   return View();

}

 

The method actually returns ActionResult, but ActionResult is ultimately the base class of ViewResult. There are a number of overloads for the View method, as shown here:

 

protected internal ViewResult View();

protected internal ViewResult View(object model);

protected internal ViewResult View(string viewName);

protected internal ViewResult View(string viewName,

   object model);

protected internal ViewResult View(string viewName,

   string masterName);

protected internal virtual ViewResult View(string viewName,

   string masterName, object model);

 

When creating a view, the controller can specify the data to be used to flesh out the view, the name of the view, and, optionally, the master name. This information is packed inside a newly created ViewResult object that is then returned from the method and, after that, from the controller (see Figure 1).

 

protected internal virtual ViewResult View(string viewName,

   string masterName, object model)

{

   if (model != null)

   {

       this.ViewData.Model = model;

   }

   ViewResult result = new ViewResult();

   result.ViewName = viewName;

   result.MasterName = masterName;

   result.ViewData = this.ViewData;

   result.ViewEngine = this.ViewEngine;

   result.TempData = this.TempData;

   return result;

}

Figure 1: The implementation of the main View method.

 

What happens next? Who consumes the information in the ViewResult object? Who assembles the markup to be incorporated in the HTTP response?

 

In the ASP.NET MVC Framework, the execution of any controller method is monitored by a special component known as the controller action invoker. The invoker does two things. First, it executes the controller s method and saves the action result. Next, it processes the action result. The whole chain of calls is shown in Figure 2. Note that the figure simply brings up the key steps and has more of a high-level view. For details, take a look at the ASP.NET MVC Framework source code or reverse-engineer it using .NET Reflector.

 


Figure 2: The call stack for the execution of a controller s method.

 

Processing the ViewResult Object

Figure 3 shows the definition of the ViewResult class the object returned by any View method invoked at the end of any controller method. As you can see, it s a pretty simple class that aggregates together a few attributes. In particular, you ll find the name of the view, the data to pass to the view, and, more importantly, a reference to the view engine. The view engine is the class that will ultimately consume all the information in the ViewResult object in order to produce the response for the caller.

 

public class ViewResult : ActionResult

{

   // Methods

   public ViewResult();

   public override void ExecuteResult(ControllerContext context);

   // Properties

   public string MasterName { get {...}; set {...}; }

   public TempDataDictionary TempData { get {...}; set {...}; }

   public ViewDataDictionary ViewData { get {...}; set {...}; }

   public IViewEngine ViewEngine { get {...}; set {...}; }

   public string ViewName { get {...}; set {...}; }

}

Figure 3: The ViewResult class.

 

You ll also see in Figure 3 that the ViewResult class has a method named ExecuteResult. This method is responsible for triggering the process that generates the view, as in this listing:

 

public override void ExecuteResult(ControllerContext context)

{

 :

 string viewName = !String.IsNullOrEmpty(this.ViewName)

          ? this.ViewName

          : context.RouteData.GetRequiredString("action");

 ViewContext viewContext = new ViewContext(

     context, viewName, this.MasterName, this.ViewData,

      this.TempData);

 this.ViewEngine.RenderView(viewContext);

}

 

After ensuring that all the required information is available, and throwing an exception if not, the method finds out the effective name for the view defaulting to a view name that matches the name of the current action. View name, master, and data for the view are then packed in to a helper ViewContext class, which becomes the input of the RenderView method of the view engine.

 

Locating the View Engine

So far I ve repeatedly addressed the view engine as an important piece of the puzzle, but I haven t said much about who defines that and how it works. The controller class owns a ViewEngine public property that references the engine to be used to obtain a response for the user after a request is made. Here s the implementation of the ViewEngine property in the Controller class:

 

public IViewEngine ViewEngine

{

   get

   {

       if (this._viewEngine == null)

       {

           this._viewEngine = new WebFormViewEngine();

       }

       return this._viewEngine;

   }

    set

   {

       if (value == null)

       {

           throw new ArgumentNullException("value");

       }

       this._viewEngine = value;

   }

}

 

As you can see, the property is set to an instance of the WebFormViewEngine class if not otherwise set. This means that, by default, the class responsible for generating the view is WebFormViewEngine a class that implements the IViewEngine interface:

 

public interface IViewEngine

{

   // Methods

   void RenderView(ViewContext viewContext);

}

 

Because of the interface, you can consider replacing this default view engine with your own. All that you have to do in this case is create a new class that implements the IViewEngine interface. Once you ve done this, you programmatically set the ViewEngine property of the controller to an instance of the new class.

 

Building a custom view engine is not a common task for every developer. As of Preview 5 of the MVC Framework, Microsoft is using the Web Forms view engine. Other engines contributed by the community are listed at http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=Documentation&referringTitle=Home. Let s learn more about the default Web Forms view engine.

 

The Web Forms View Engine

There s still a lot of the Web Forms model in the ASP.NET MVC Framework (as of Preview 5 of the framework, which was available at the time of this writing).

 

In the WebFormViewEngine class, the RenderView method retrieves the URL of the page to use as a template to generate the markup for the browser. By default, in the ASP.NET MVC Framework you define views through ASPX pages located in a particular folder. The folder is named after the controller that controls the view and is placed below a Views directory. For example, with reference to the About controller method I showed at the beginning of the article, the subsequent view would be generated by an about.aspx file located under Views\Home, where Views is the root directory of view pages and Home is the name of the controller that governs display and action of the about page.

 

Once the URL of the view page is known, the engine simply places the following call to one of the building block classes of the standard ASP.NET run-time environment:

 

object page = this.BuildManager.CreateInstanceFromVirtual

 Path(viewLocation, typeof(object));

 

More exactly, the BuildManager object here is not necessarily the original ASP.NET build manager, but simply a wrapper class that can be replaced in case of need. However, in the default case if you don t change anything the ASP.NET MVC Framework s build manager is simply the original ASP.NET build manager.

 

What s the purpose of this class? The build manager is ultimately responsible for taking the URL to an ASPX resource and producing an instance of a class that inherits from System.Web.UI.Page. The build manager triggers the compilation process that transforms plain HTML markup into ASP.NET literal controls.

 

Before exiting, the RenderView method on the view engine class ensures that the object it got back from the build manager is a ViewPage class, then calls a method on it. Finally, this rendering method simply does the following:

 

this.ProcessRequest(HttpContext.Current);

 

Guess what? ProcessRequest is exactly the method that operates in classic ASP.NET when it comes to processing an HTTP request. The method receives the current HTTP context and outputs any HTML by accessing the Response s output stream.

 

Using Server Controls in the ASP.NET MVC Framework

Having found out that the default view engine in the ASP.NET MVC Framework is nothing more than a Web Forms-based engine, what about server controls? Can you seriously use them, even in an ASP.NET MVC page? Let s try in a view page the code shown in Figure 4.

 

 "MainContent" runat="server">

   

About Us

   

       Send your feedback.

   

   

       

       

         OnClick="Button1_Click" />

   

       


   

       

   

Figure 4: Server controls in an ASP.NET MVC page.

 

It s not really code that differs from a regular ASP.NET page:

 

public void Button1_Click(object sender, EventArgs e)

{

 string text = "Thank you for your feedback: "

   + TextBox1.Text;

 Label1.Text = text;

}

 

And it works like it would in a regular ASP.NET page (see Figure 5). When you use server controls in an ASP.NET MVC page, you force the system to generate and carry on the viewstate and enable yourself to fill view pages with event handlers.

 


Figure 5: Server controls in action in an ASP.NET MVC page.

 

This said, I must emphasize that not everything that is doable should be done. The ASP.NET MVC Framework and classic ASP.NET are alternative approaches to the development of Web pages. Either you choose one or the other. Using server controls in an MVC context puts at risk your initial (excellent) intention of using separation of concerns profusely within the application. In an MVC application, all that you focus on are controllers and their actions and a view engine to render it out to HTML. So using server controls in an ASP.NET MVC page is perfectly possible, and it works.

 

Conclusion

The ASP.NET MVC Framework decouples the view from the controller and makes it possible for you to focus on the actions to execute in response to an HTTP request from the HTML to be served to the browser. So the View method on the Controller class starts the process that will take the view engine to generate the markup for the client. By default, the view engine is based on the same Web Forms run-time environment that you know from classic ASP.NET. In this article, I first went through the nitty-gritty details of the view generation process, then demonstrated that ASP.NET server controls also can be used in ASP.NET MVC.

 

Dino Esposito is an architect at IDesign, where he specializes mainly in ASP.NET, AJAX, and RIA solutions. Dino is the author of Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008). He also wrote Programming ASP.NET 3.5 Core Reference, also for Microsoft Press. Late-breaking news is available at http://weblogs.asp.net/despos.

 

 

 

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