Chapter 4. Razor and ASP.NET MVC

First introduced in early 2008, ASP.NET MVC provided an alternative approach to developing web applications on the ASP.NET platform. As the name indicates, ASP.NET MVC embraces the Model-View-Controller (MVC) architecture, an approach favoring the separation of concerns between application layers. ASP.NET MVC views are much more HTML-focused than views in other frameworks such as Web Forms. Razor complements ASP.NET MVC quite nicely because its simplistic and elegant syntax produces a seamless transition between markup and code, allowing the markup to remain the main focus and not fade into a sea of code-specific syntax.

This chapter will provide a brief introduction to the ASP.NET MVC framework as well as demonstrate how to leverage the Razor syntax to create clean and effective ASP.NET MVC views.

Installing ASP.NET MVC

To begin developing ASP.NET MVC websites using Razor, you’ll need to have at least ASP.NET MVC version 3. The Web Platform Installer is the easiest way to install ASP.NET MVC 3.

To begin installation using the Web Platform Installer, visit the ASP.NET MVC website and find the big button that says “Install Visual Studio Express” (or something similar).

Regardless of what you have installed on your system prior to running the Web Platform installer, clicking Install will download and install everything you need to start developing ASP.NET MVC 3 applications using Razor.

The Model-View-Controller Architecture

The MVC architecture comprises three layers, each with unique and independent responsibilities:

Model

Represents the core business/domain data and logic, typically with POCOs (Plain Old CLR Objects), devoid of technology-specific implementations

View

Responsible for transforming a Model or Models into a response sent to the user (typically HTML)

Controller

Interprets incoming web requests, managing the interaction between the user and the model (typically through database queries, web services calls, etc.) and building a Model for the View to consume

In the course of an ASP.NET MVC website request, the platform locates and executes the corresponding controller method, also called the action. The result of this action is almost always an ActionResult. The most widely used type is the ViewResult—an ActionResult indicating which view the framework should respond to the request with. Following ASP.NET MVC’s strict separation of concerns, it is not the controller that is responsible for rendering HTML. Instead, the ASP.NET MVC framework passes the ActionResult from the controller to the View Engine, which handles the conversion of a ViewResult into rendered HTML to send back to the client.

ASP.NET MVC View Engines

The initial ASP.NET MVC releases shipped with the Web Forms View Engine, which allowed developers to create views with the popular and mature Web Forms syntax. Ironically, the very aspect that makes the Web Forms View Engine such a great fit for new ASP.NET MVC developers—its popularity—is also its biggest drawback. Despite the fact that they are based on the same underlying platform (the ASP.NET framework), the ASP.NET MVC and Web Forms approaches and architectures are fundamentally different in many ways. Thus, while the Web Forms syntax may be second nature to developers creating applications on the Web Forms platform, those same developers may be tempted to try to leverage the Web Forms platform in MVC views…with disastrous results.

The Razor View Engine

The early 2011 ASP.NET MVC 3 release added the Razor View Engine as part of the framework, offering an alternative to the Web Forms View Engine. The Razor View Engine provides developers with a full stack of APIs that leverage the powerful and lightweight Razor syntax, allowing developers to write simpler views that more effectively and efficiently target the MVC architecture.

The Razor View Engine works in much the same way as the Web Forms View Engine, in that views are stored in physical files in the same conventions-based folder structure. Developers author these files using the Razor syntax to create a hybrid document of code and markup. Then at runtime—also as with the Web Forms View Engine—the ASP.NET MVC framework compiles the Razor templates into .NET classes and executes the compiled classes to render responses for requests to the site. The following sections explain each of these steps in detail.

Differentiating Razor syntax and API implementations

It is important to note that the Razor syntax, the Razor API, and implementations built on top of the Razor API are three different things. Previous chapters showcased WebMatrix examples of the Razor syntax as well as WebMatrix implementations built on top of the Razor API. Likewise, the Razor View Engine also leverages the Razor syntax and API, but also adds subtle enhancements to better suit ASP.NET MVC views.

Though many of the examples shown in previous chapters are relevant to Razor views in ASP.NET MVC, ASP.NET MVC implements quite a few features either differently or not at all. As you read on, keep this in mind, and look for the portions of the book that point out and explain these differences.

Implementing a Blog Site Using ASP.NET MVC

To illustrate how to write Razor views in an ASP.NET MVC application, let’s revisit the WebMatrix blog website and rewrite it “the MVC way.” To start out, open up Visual Studio and choose File → New Project..., and then select the ASP.NET MVC 3 Web Application option. Name the new project MvcRazorBlog, as shown in Figure 4-1.

Creating a new MVC application
Figure 4-1. Creating a new MVC application

Then, from the New ASP.NET MVC 3 Project Wizard, select the Empty site template and the Razor View Engine (these should be the default settings); Figure 4-2 provides an example.

The ASP.NET MVC 3 Project Wizard
Figure 4-2. The ASP.NET MVC 3 Project Wizard

Once completed, your project should resemble the directory structure shown in Figure 4-3.

Folder structure for a new ASP.NET MVC project
Figure 4-3. Folder structure for a new ASP.NET MVC project

The Model

The blog site example from the WebMatrix chapter didn’t involve an official “model.” That is, you never created any classes to hold and manage data; all of the data interaction used method calls directly to the database and saved the results in local dynamic variables. WebMatrix can get away with accessing and using the data directly because of the page-based architecture it is built on; every page is responsible for its own data access (with assistance from helper objects) and for manipulating that data.

ASP.NET MVC’s architecture dictates that the Model and the View are two separate entities, so in order to demonstrate the Razor syntax within ASP.NET MVC’s Razor View Engine, you should create a model that can hold and manage the site’s data. Since you’ve already implemented the blog site once, you already know what data the site uses. To create the Model for the ASP.NET MVC blog site, add a new class named Post to the website’s Models folder, with the following code:

namespace MvcRazorBlog.Models
{
    public class Post
    {
        public long ID { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }
    }
}

Since the blog site doesn’t require very complex data, the Post class is all that’s needed at this point. Once it’s in place, you can create the Controller that will populate the Post class with data from the database and pass it to the View.

The Controller

The default Empty site template chosen earlier expects a controller named HomeController, which ASP.NET MVC adds to the Controllers folder by convention. To create an empty controller, right-click on the Controllers folder and select Add → Controller…, entering the name HomeController.

The wizard will ask you if you’d like it to generate Create, Update, Delete, and Details actions for you. We won’t use those actions in this book, but feel free to let the wizard generate them for you if you’re interested to see what they look like. The wizard will create a new class file with one action named Index:

using System.Web.Mvc;

namespace MvcRazorBlog.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

According to ASP.NET MVC’s default routes, the HomeController’s Index action handles requests for the site’s home page (the root of the site without specifying any file or folder names). The behavior we want to implement in the Index action directly correlates to logic used in the sample WebMatrix site’s Default.cshtml page in Chapter 2. So, the next step is to reproduce the same data access logic that the Default.cshtml page uses to retrieve Post data.

Data access with Entity Framework code first

For better or worse, ASP.NET MVC does not offer the same Database object included in the WebMatrix platform. Instead, Microsoft’s Entity Framework 4.1 (and up) and its Code First functionality make data access just as easy as it is in WebMatrix, or perhaps more so.

To get started with Entity Framework Code First, you’ll need to install it using the NuGet Package Manager, a Visual Studio extension installed as part of the ASP.NET MVC 3 install process.

After it’s installed, the only other step you need to take in order to use the Entity Framework Code First framework is to create a custom class that derives from the System.Data.Entity.DbContext class, which tells Entity Framework which objects you’d like to persist and retrieve from the database. A working DbContext implementation can be as simple as the following:

using System.Data.Entity;
using MvcRazorBlog.Models;

public class BlogContext : DbContext
{
    public DbSet<Post> Posts;
}

This code extends the DbContext class, exposing a single property (Posts) of type DBSet<Post>. It is the DbSet<Post> portion of this property that indicates to Entity Framework that you’d like to use the Post class as your model for data access and that these objects can be found in the Posts table (corresponding to the name of the property) in the database.

That’s all that’s needed in regards to setup and configuration; Entity Framework relies on “convention over configuration” to determine the rest. Entity Framework will even create the database for you if it does not exist when you attempt to access it!

Querying an Entity Framework Code First data context is even easier than writing one. All you need to do is create an instance of a DbContext and start making standard LINQ calls against the DbSet<TModel> properties defined on the context.

Thus, retrieving all of the Posts from the Blog database from the HomeController’s Index action is trivial:

public class HomeController {
    public ActionResult Index()
    {
        var posts = new BlogContext().Posts;
        return View("Index");
    }
}

Once the Index action retrieves the blog post data from the database, it needs to select the appropriate View and pass the post data to that View. Since the MVC pattern deliberately decouples views and controllers, the controller cannot directly communicate with the selected view to provide the view with the data it needs. In order to get around this limitation, ASP.NET MVC relies on an intermediary dictionary via a property named ViewData. Controllers and views both contain this property, so it is easily accessible in both layers. Here is the updated Index action, adding a line of code to assign the blog post data to the ViewData dictionary for use in the views that you will create in the coming sections:

public class HomeController {
    public ActionResult Index()
    {
        var posts = new BlogContext().Posts;

        ViewData["Posts"] = posts;

        return View("Index");
    }
}

Though passing values to views through the ViewData dictionary works just fine, “magic values” (string constants referenced in multiple places) like "Posts" can become difficult to manage and maintain. Additionally, the ViewData dictionary is not strongly-typed, so Visual Studio cannot provide you with IntelliSense on any values passed into it. Instead of the ViewData dictionary, the preferred approach for passing data from controllers to views is through the model parameter of the View() helper method.

Below you can see the Index action updated to pass the Posts data via the View() helper method:

Index action retrieving blog posts and passing them to the view
public ActionResult Index()
{
    var posts = new BlogContext().Posts;
    return View("Index", posts);
}

At this point—even though you have not yet created the View required to render HTML to the user—you should be able to verify that this code works by placing a debug breakpoint on the final return View("Index"); line and hitting F5 to run the website. If everything works, execution should stop at the breakpoint and the posts variable should contain a list of Post objects with the data from your database!

Convention versus Configuration

To make website development easier and help developers be more productive, ASP.NET MVC relies on the concept of “convention over configuration” whenever possible. What this means is that, instead of relying on explicit configuration settings, ASP.NET MVC simply assumes that developers will follow certain conventions as they create website components.

The previous section applied several conventions when creating the HomeController and its Index action. The first convention was placing the controller in the project’s Controllers folder. Though not required for the application to compile and function, keeping your controllers in the Controllers folder is a standard practice. The second convention was the name of the controller, HomeController. Since it is a very good practice to give classes descriptive names, standard conventions recommend applying the “Controller” suffix to controller class names. When referring to the controller throughout the application, however, it is much more straightforward to simply refer to “Home” instead of the full name, “HomeController.”

The ASP.NET MVC framework addresses this conflict during the application’s startup phase, when it searches through all of the website’s assemblies, registering classes that extend ASP.NET MVC’s ControllerBase base class (using the base class as yet another convention). As the framework locates classes that derive from ControllerBase, it adds them to its internal dictionary using the class’s name—after removing the “Controller” suffix—as the dictionary key. Thus, even though the controller is named “HomeController,” the rest of the application can simply refer to it as “Home.”

At first glance, the concept of convention over configuration may seem trivial. However, when taken altogether over the course of a large site, many of these seemingly small or meaningless optimizations can really add up to significant time savings, improved code readability, and increased developer productivity.

Authoring ASP.NET MVC Views with the Razor Syntax

Since WebMatrix and ASP.NET MVC Razor views share the Razor syntax to create views, the easiest way to create a view in the demo ASP.NET MVC blog site is to copy the markup we already created in the WebMatrix example. First, however, you need to create a new view to contain the copied markup.

Adding Razor Views to an ASP.NET MVC Application

The ASP.NET MVC platform adds several convenient and helpful extensions to the Visual Studio IDE. One of the extensions adds two new context menu items, named Add View and Go to View, that show up when you right-click on a controller action. These menu items make it very easy to create and navigate to the views associated with an action.

The Add View context menu item is the quickest and easiest way to create a new view. To create a new view for the Index action in the HomeController of the demo blog application, simply right-click anywhere within the Index action, and then choose Add View. This should pop up the Add View Wizard.

The Add View Wizard prepopulates several of the fields by using conventions. In this case, the value in the prepopulated “View name” field is “Index,” the name of the controller action from which we clicked on the Add View context menu. The “View engine” field lets developers choose which syntax to use for creating views. The default view engine is “Razor (CSHTML),” which means the Razor C# syntax (as opposed to, say, the Razor Visual Basic .NET syntax).

Ignore the rest of the fields in the wizard; later sections of the book will revisit them in detail. For now, just click the Add button and let’s write a view!

Clicking the Add button tells Visual Studio to create a new view file for you at the appropriate location within the project. Figure 4-4 shows the contents of the project’s Views folder after the Add View dialog executes.

Views folder structure
Figure 4-4. Views folder structure

As you can see, the Views folder contains child folders named Home and Shared. Why are these folders named this way? You guessed it: yet another convention! By default, ASP.NET MVC assumes that all website views exist somewhere underneath the Views folder. Further, ASP.NET MVC looks for child folders underneath the root Views folder with folder names corresponding to the names of controllers in the application. Using all of this information—along with the information we provided—the Add View dialog chose to create a file named Index.cshtml in a new folder under the root Views folder named Home (corresponding to the HomeController controller class the Add View menu action originated from). Leveraging the power of conventions, Visual Studio was able to do all of this with just two clicks!

Why the .cshtml file extension?

In the course of rendering a view, the ASP.NET MVC runtime will use the view’s file extension to determine how to compile the markup in the view. For instance, ASP.NET MVC can safely assume that a file with the .aspx extension uses the Web Forms syntax, whereas a view with the .cshtml extension contains Razor markup. Both ASP.NET MVC Razor views and WebMatrix Web Pages use the .cshtml extension for Razor views that leverage the C# language and .vbhtml for Razor views that leverage Visual Basic .NET. ASP.NET MVC supports both languages equally—they can even be used together in the same project!

Writing ASP.NET MVC Razor View Markup

After creating the new file, replace the entire contents of this new view with this markup:

<h1>My Blog</h1>

@foreach(var post in Model) {
    <div>
        <h3>@post.Title</h3>
        <div>@post.Body</div>
    </div>
}

This should look familiar; it’s very similar to the markup you used to create the home page in the WebMatrix blog site. The biggest difference is that, instead of pulling the posts data from the database, the markup iterates over the View’s Model property, which represents the model data passed to the View from the Controller in Data access with Entity Framework code first.

At this point, you have everything in place to generate a web page: the Controller is retrieving data from the database and passing that data to the View, which the View can then use to render dynamic HTML. To see it in action, hit F5 to execute the website. If you have data in the Posts database, it should render something similar to the following:

<h1>My Blog</h1>

    <div>
        <h3>Test Post #1</h3>
        <div>This is the first test post</div>
    </div>
    <div>
        <h3>Test Post #2</h3>
        <div>This is the second test post</div>
    </div>
    <div>
        <h3>Test Post #3</h3>
        <div>This is the third test post</div>
    </div>

Strongly-Typed Views

The Model property present in the WebViewPage base class from which all ASP.NET MVC views derive is dynamically-typed by default. This means that it uses .NET 4.0’s dynamic type, delaying the specification of the type until runtime. Thus, Controller Actions and other parts of the ASP.NET MVC framework can populate this field with any type and the views can apply the concept of “duck-typing” to work with a variety of types. As long as the model object contains the properties with the name that the view expects, any type will do!

If this “fast and loose” approach concerns you, you are in good company. You are also in luck: ASP.NET MVC adds the @model keyword to Razor’s vernacular, which allows views to specify the type of their Model property. The @model keyword follows the syntax @model Class Name.

To show the @model keyword in action, let’s modify the previous example and turn it into a strongly-typed view by specifying a @model:

A strongly-typed view
@model IEnumerable<MvcRazorBlog.Models.Post>

@section Header {
    <h1>My Blog</h1>
}

@foreach(var post in Model) {
    @RenderPage("~/Posts/_Post.cshtml", new { post = post })
}

And just like that, the Model property is now strongly-typed as an IEnumerable<MvcRazorBlog.Models.Post>, ready for use without additional casting.

By default, all ASP.NET MVC Razor views inherit from the System.Web.Mvc.WebViewPage class. However, views that specify a model type using the @model keyword inherit from the generic version of WebViewPage: System.Web.Mvc.WebViewPage<TModel>. With the exception of a few Razor keywords outlined in the first chapter, the bulk of ASP.NET MVC’s view-related functionality (such as the @Html and @Url helpers) exists as properties or extension methods off of the WebViewPage class.

What the @model keyword actually does is tell ASP.NET MVC to add a generic parameter to the base page type. In other words, an ASP.NET MVC view without the @model keyword derives from the WebViewPage base class like so:

public class Index : WebViewPage {
    /* Class stuff in here */
}

But when the @model keyword is applied, as in the previous example, Razor adds the specified type as a generic parameter to the generated class. For example:

@model IEnumerable<MvcRazorBlog.Models.Post>

will generate the view:

public class Index : WebViewPage<IEnumerable<MvcRazorBlog.Models.Post>> {
    /* Class stuff in here */
}

Changing the Base Class

Despite the great functionality it provides, you will inevitably come across scenarios where the System.Web.Mvc.WebViewPage class simply doesn’t cut it. In cases where you need more functionality than the WebViewPage class offers, Razor provides the @inherits keyword, which lets you specify any base class you want—even your own custom base class. One of the most common uses of the @inherits keyword is to specify a custom base class that extends the WebViewPage class, adding custom functionality through properties and methods.

Let’s say that you have created a bunch of helper methods and you want some kind of shortcut to access your helper methods from within your views. The quickest way to provide easy access to custom helper methods is to write extension methods around the @Html or @Url, since they already exist in the WebViewPage class. However, if you like to keep your helper methods separate from the core ASP.NET MVC functionality, you can choose to create your own helper object that acts much like @Html or @Url, and then add an instance of that custom helper object to a custom base class. The BlogHelper class below shows an example of such a custom helper object. The custom base class (BlogViewPage) exposes the custom helper object via a custom property:

public class BlogHelper
{
    private readonly UrlHelper _urlHelper;

    public BlogHelper(UrlHelper urlHelper) {
        _urlHelper = urlHelper;
    }

    public MvcHtmlString BlogPostLink(int postId) {
        var tag = new TagBuilder("a");
        tag.AddCssClass("blog-post");
        tag.Attributes["href"] = _urlHelper.Action("Post", "Posts");

        return new MvcHtmlString(tag.ToString());
    }
}
namespace MvcRazorBlog {
    public abstract class BlogViewPage : BlogViewPage<dynamic>
    {
    }

    public abstract class BlogViewPage<TModel> : WebViewPage<TModel>
    {
        protected BlogHelper Blog
        {
            get { return new BlogHelper(Url); }
        }
    }
}

The first thing you’ll notice about the custom base class BlogViewPage is that there are actually two of them—one that accepts a generic TModel parameter, and one that is not generic. The reason you should create both is so that you continue to have the option of strongly-typed views. Without the generic version of this class, you would not be able to use the @model keyword to specify the view’s Model type. Likewise, if the nongeneric BlogViewPage doesn’t exist, all of the views that depend on the BlogViewPage custom base class would be required to specify a TModel parameter (i.e., they’d need to be strongly-typed views).

The next thing you’ll notice about BlogViewPage is how tiny it is. Because the aim is to merely extend the core ASP.NET MVC functionality and not replace it, BlogViewPage can derive from ASP.NET MVC’s WebViewPage<TModel> and in doing so, carry along all of the core functionality. With the core functionality in place, BlogViewPage’s only goal is to expose the new custom Blog property, a simple property that just returns a new instance of the custom BlogHelper helper class.

With the BlogViewPage created, referencing it is as simple as adding the @inherits keyword to any Razor views that require it:

View inheriting from custom base class
@inherits MvcRazorBlog.BlogViewPage

<span>Here's a link to Post #123:</span>
@Blog.BlogPostLink(123)

Here you can see that the view inherits from BlogViewPage, then refers to the @Blog.BlogPostLink() method on the custom Blog property.

Warning

ASP.NET MVC does not let you specify both the @inherits keyword and the @model keyword in the same view.

Since ASP.NET MVC does not let you specify both the @inherits keyword and the @model keyword in the same view, you need to specify the model type (the TModel generic parameter) directly in the @inherits statement in order to refer to the strongly-typed version of a custom base class. Thus, to change the previous example from weakly-typed to strongly-typed, you would change its @inherits statement like so:

@inherits MvcRazorBlog.BlogViewPage<AdminViewModel>

The custom base class approach offers greater levels of customization and productivity in your websites. Custom base classes are not only easy to create and reference, but they are also a great way to provide quick and easy access to customized, application-specific functionality across all views in an application. Whenever the default WebViewPage class just doesn’t cut it, consider implementing your own base class!

Applying Custom Base Classes to Multiple Views

When all of your views need to derive from the same custom base class, having to add the @inherits keyword to all of them is not only tedious, but it’s also difficult to maintain. Luckily, ASP.NET MVC offers an alternative approach to specifying the default base class for all Razor views: the system.web.webPages.razor > pages > pageBaseType configuration attribute. To modify this attribute, open the web.config file located in your application’s Views folder, and then locate the line that looks like this: <pages pageBaseType="System.Web.Mvc.WebViewPage">. This is where ASP.NET MVC’s default base page type comes from. To use your own base page type, simply replace the reference to System.Web.Mvc.WebViewPage with the full type name of your base class type (e.g., <pages pageBaseType="MvcRazorBlog.BlogViewPage">). After saving the modified web.config, every view in your application should use your custom base page type.

Layouts and Content Pages

You may have noticed that the markup in Strongly-Typed Views included an <h1> header tag and a list of <div>s with blog post content, but no surrounding HTML document markup like <html> or <body> tags. This is because when you created the new view and left the “Use a layout or master page” option checked and the text box empty you told ASP.NET MVC to create a “content page,” a page that relies on a Layout to define the structure of the page.

Chapter 3 discusses Razor layouts and content pages in depth and all of those concepts apply to ASP.NET MVC Razor views as well. In addition to that functionality, however, ASP.NET MVC adds an additional layer of abstraction and helper methods to simplify working with Razor views.

The most significant example of ASP.NET MVC Razor view helpers is the HtmlHelper.Partial() method, whose syntax is very similar to the Razor @RenderPage() method, but instead of accepting a static filename, the HtmlHelper.Partial() method expects a simple view name, which ASP.NET MVC then uses to locate and render the appropriate view.

For example, Strongly-Typed Views contains a call to @RenderPage():

@RenderPage("Posts/_Post.cshtml", post)

In this snippet, Posts/_Post.cshtml refers to a physical file in the website. In comparison, this same call as an ASP.NET MVC HtmlHelper.Partial() would look like this:

@Html.Partial("_Post", post)

The Partial() method call still refers to the same partial view, except the location and exact filename of that view are now abstracted away. That is, instead of the view dictating the exact file location of Posts/_Post.cshtml, ASP.NET MVC expects a request to render the partial view named _Post, leaving the implementation details of the _Post view up to the framework to figure out. Though in this instance ASP.NET MVC will, in fact, execute the Posts/_Post.cshtml view, consider what would happen if a developer decided to move the view to a different folder or even rewrite the view in VB.NET (thus changing the view’s extension to .vbhtml). That’s just fine; as long as ASP.NET MVC is able to match the view name to a file that it knows about, it will render it no matter what its physical location or what language it’s written in.

Razor View File Locations

At this point, you may be wondering just how ASP.NET MVC is able to match the name of a view to its physical location in the filesystem. For that matter, how does ASP.NET MVC differentiate between two views that share the same file name, yet reside in different folders? The answer to both of these questions is the same: the Razor View Engine!

When a controller action returns a ViewResult, ASP.NET MVC knows that it needs to render a view, so it asks all of its registered view engines if they can figure out how to locate and render the requested view. Between the routing information for the current request and the ViewResult, the view engines should have all the information they need: the view name and the name of the controller that handled the request. By convention, all ASP.NET MVC views live in subfolders under the ~/Views folder.

In one of the previous examples, the HomeController requested the view named Index. Let’s see how the Razor View Engine tries to locate the Index view.

Controller Views

The first place the Razor View Engine will look for a view is in the folder with the same name as the controller that handled the request. Given the request from the HomeController for the Index view, the view engine will check if the C# Razor template ~/Views/Home/Index.cshtml exists. If not, the engine tries again, this time looking for the VB.NET Razor template named ~/Views/Home/Index.vbhtml. Though C# and VB.NET are the only languages Razor currently supports, the same process would apply for any new languages that might be added (for example, the engine might look for ~/Views/Home/Index.fshtml for an F# template if such an implementation existed).

When the view engine locates a file, it stops looking immediately and returns the first file it found. Otherwise, it continues down its list of search paths to look in the Shared folder.

Locating Razor Views

Armed with all the information about the controller that handled the current request, the Razor View Engine relies on the default view locator logic to build a list of possible locations in which the requested view may reside. After compiling this list, the view engine simply iterates over it, returning the first match it finds. As you might expect, the view engine prefers more specific views—views defined in the folder with the same name as the Controller that requested them—over views in the Shared folder, so the more specific views will appear higher on the list than shared views.

This example illustrates this point, showing what the list of possible view locations might look like in order to locate a request for the Index view that generated from a controller named HomeController:

The view engine’s view candidates
~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

Thus, if the ~/Views/Home/Index.cshtml view exists, the view engine will choose it and ignore the rest of the list. If, however, ~/Views/Home/Index.cshtml does not exist, but ~/Views/Shared/Index.cshtml exists instead, the view engine will continue searching down the list, checking each entry, until it discovers that ~/Views/Shared/Index.cshtml is valid and chooses it.

Shared Views

You may have noticed that the folder list includes references to the Shared folder. The ~/Views/Shared folder contains views that can be reused by multiple controllers. The Shared folder is created along with the rest of the initial application artifacts by the ASP.NET MVC website template and initially contains the basic layout and error-handling views (_Layout.cshtml and Error.cshtml, respectively), two perfect examples of reusable views.

Views in ASP.NET MVC Areas

The Areas feature of ASP.NET MVC allows a web application to be split up into multiple sections (“Areas”), enabling developers to work on each section individually in relative isolation. Though an in-depth discussion of ASP.NET MVC Areas would be outside the scope of this book, Areas are effectively a “website within a website” and, as such, it is worth discussing how Areas affect the views that are created within them.

For example, let’s add an Area named “Administration” to the demo blog site. To add an Area, right-click on the ASP.NET MVC project and select the Add... context menu option, which should pop up the submenu shown in Figure 4-5 and specify the Area name “Administration” when prompted.

Add Area context menu option
Figure 4-5. Add Area context menu option

As Figure 4-6 shows, ASP.NET MVC Areas define a folder structure within the main website that follows the same standard Controllers, Models, and Views convention. The primary difference is that the root folder of this structure is not the root folder of the ASP.NET MVC application.

Website folder structure with an Area
Figure 4-6. Website folder structure with an Area

Since Areas use the same folder structure convention, the only significant change they introduce in regard to Views is adding their folders to the list of search folders that the View Engine uses to locate the correct view. For example, a ViewResult from an action within the Administration Area’s DashboardController would modify a default search folder list:

~/Views/Dashboard/Index.cshtml
~/Views/Dashboard/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

to include the Views folder within the Admin area:

~/Areas/Administration/Views/Dashboard/Index.cshtml
~/Areas/Administration/Views/Dashboard/Index.vbhtml
~/Areas/Administration/Views/Shared/Index.cshtml
~/Areas/Administration/Views/Shared/Index.vbhtml
~/Views/Dashboard/Index.cshtml
~/Views/Dashboard/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml

Outside of the modified search path, developing views within Areas is the same as developing views located in the main website.

Tip

The way the Razor View Engine and the Web Forms View Engine look for views is almost exactly the same. Both engines look in the folder with the name of the current controller as well as the Shared folder. Both engines also respect the Areas folder as well. In fact, the primary difference in the way the two view engines locate views is the file extension they’re looking for: the Razor View Engine searches for .cshtml and .vbhtml files while the Web Forms View Engine searches for files with the .aspx and .ascx extensions.

Html and Url Helper Classes

While previous chapters discussed almost everything you need to know to write effective ASP.NET MVC Views, ASP.NET MVC offers a number of additions to the core Razor syntax and API. The most helpful additions are the HtmlHelper and UrlHelper classes, exposed in ASP.NET MVC Views as the Html and Url properties, respectively. These two helper methods provide much-needed access to ASP.NET MVC’s more advanced and decoupled ways of interacting with Models and other Views.

For example, rather than manually building anchor tags and URLs, ASP.NET MVC offers the HtmlHelper.ActionLink() method which accepts a number of varying parameters and emits a complete anchor tag (<a>) to the page. The snippet below shows the HtmlHelper.ActionLink() method in action:

@Html.ActionLink("Site Members", "Members", "Admin")

This particular overload of the HtmlHelper.ActionLink() method contains three parameters:

  1. The link text to show as the inner HTML within the anchor tag

  2. The name of the Action referred to (in this case, the "Members" action)

  3. The name of the Controller containing the Action in parameter #2 (in this case, the "AdminController" class)

When executed during view rendering, the snippet produces the following HTML:

<a href="/Admin/Members">Site Members</a>

While ASP.NET MVC gives developers endless control over the HTML that is rendered to the client, helper methods like these provide a more declarative—and therefore, more maintainable—way to implement site functionality.

The HtmlHelper.ActionLink() example is just one of many useful helper methods available on the Html and Url View properties. Though we will not review all of them at this point, future examples (in fact, almost any ASP.NET MVC example you will see) will leverage these properties quite heavily, so keep an eye out!

ASP.NET MVC’s Razor View Page Rendering Life Cycle

ASP.NET leverages a “just in time” (JIT) compilation model in which views are written and deployed as regular text files and only converted into code for ASP.NET to consume at the last possible moment. The “last possible moment” is generally the first request for a view by a visitor to the site. Despite having an entirely different architecture than Web Forms, Razor views share the same deployment and compilation life cycle. The only difference between the two rendering engines is how they turn a text file into an executable class for the web application to use in rendering responses.

Precompiling Razor Views

While the JIT compilation feature of ASP.NET makes it trivial to modify views in a deployed site (with even a simple text editor), this means that the ASP.NET framework must compile them in real time on the web server. This approach has several drawbacks:

Increased wait times

The first visitors to each page must wait while the view is compiled and rendered. Though compilation typically takes just a few seconds, this delay can be unacceptable for businesses that demand total rendering times of 2 seconds or less.

Increased server resources

Even the compilation of simple views requires server resources. While compiling a few pages on a simple site may take a small amount of CPU time and memory usage, consider a much larger website with hundreds or thousands of views with hundreds or thousands of users all waiting for their pages to finish compiling!

Delayed discovery of errors

Since text-based views are only compiled during their first request, they are essentially source code files until that time. Because of this, compilation errors are not exposed until the “last possible moment,” which effectively means that compilation errors in views will not be discovered until the website is live on the server.

Luckily, ASP.NET provides you with the ability to precompile views before they are deployed to a web server. That is, you can execute the JIT compilation—the same application that ASP.NET itself uses to compile views—on demand without having to wait for website requests. The application (named aspnet_compiler.exe) is located along with the rest of the core .NET Framework libraries and applications (typically %WINDIR%\Microsoft.NET\Framework\[framework version]). Thus, if you have the full .NET Framework installed, you have the ability to compile the views in your website, even in your local development environment.

Executing the aspnet_compiler.exe application is very straightforward. Assuming the root folder of your website project is C:\Projects\Website, execute the following line using the Visual Studio Command Prompt:

aspnet_compiler -v / -p "C:\Projects\Website"

The compiler will run for a short time (anywhere from a few seconds to a minute or more, depending on the size of your site), and emits warning and error messages as it compiles each view it finds. If you are lucky, the compiler will exit with no messages beyond the “splash screen,” indicating that all your views have successfully compiled with no warnings or errors.

Now, let’s introduce a compilation error and run the compiler again, which spits out the following error:

Test.cshtml(5): error CS0103: The name 'index' does not exist in the current context

This line says that the compiler found one error on line 5 of the file Test.cshtml: the Razor markup is trying to refer to a variable named index, but it is not defined in the page. When the compiler finds warnings or errors such as these, simply edit the respective view to fix the errors and rerun the compiler until the errors go away. Just like the .NET compilers, aspnet_compiler.exe does not alter anything in the site, so it can run repeatedly with no side effects.

Using the aspnet_compiler.exe tool, you can precompile every view in any ASP.NET website (including Web Forms pages as well) before they are accessible to users, ensuring that the code in your views properly compiles and eliminating unnecessary server resource utilization and visitor wait times.

Precompiling Razor Views in an ASP.NET MVC application

While the command line approach can be very effective, the ASP.NET MVC Web Application project type adds a more integrated option. The project file includes a property named MvcBuildViews, which, when enabled, executes the aspnet_compiler.exe as part of the build. This setting is disabled by default because of the additional time incurred by running the compiler and needs to be enabled to take effect. The setting is also not accessible via the Visual Studio user interfaces. Despite these drawbacks, enabling view compilation as part of the ASP.NET MVC website build process is quite simple:

  1. Open the website’s project file with any normal text editor other than Visual Studio (Windows Notepad is just fine)

  2. Locate the XML element named <MvcBuildViews>, whose value should be "false"

  3. Modify the value of the <MvcBuildViews> element to "true"

  4. Save and close the updated project file

  5. Reload the project in Visual Studio

Once the setting is enabled, all builds will include a new step which executes the aspnet_compiler.exe. As Figure 4-7 shows, any warnings or errors show up in Visual Studio’s error console, just as any other compile-time errors or warnings.

MVC view errors in Visual Studio’s error list
Figure 4-7. MVC view errors in Visual Studio’s error list

Compiling views from MSBuild

If you would like to avoid editing the website project file manually and don’t like the idea of adding time to every build of your website, there is a middle ground between having the MvcBuildViews setting permanently enabled or never enabled. Another application shipped with the .NET Framework, MSBuild.exe, provides the ability to execute the same compilation pipeline that Visual Studio itself uses without opening Visual Studio. What’s more, MSBuild.exe also allows project property settings (like the MvcBuildViews setting) to be overridden.

Tip

When you leave the MvcBuildViews property disabled in the project file and enable it as an MSBuild flag, you get the best of both worlds: quick compilation times while developing in Visual studio without losing the ability to discover errors in your views. Just remember: it is now up to you to run MSBuild often!

To execute MSBuild.exe, open up the Visual Studio Command Prompt (just as you did earlier for aspnet_compiler.exe), and then switch to your solution’s directory. Then, execute the following line (replacing SolutionName with the name of your solution):

msbuild /p:MvcBuildViews=true SolutionName

The command for the example blog project would be:

msbuild /p:MvcBuildViews=true MvcRazorBlog.sln

The MSBuild output includes all of the details of the build. Most importantly, the MvcBuildViews step executes the aspnet_compiler.exe you manually ran before. In addition to eliminating the need to execute aspnet_compiler.exe manually, executing aspnet_compiler.exe as part of the build script makes the compilation a first-class citizen in the build process. Now, any time a view contains an error, that error will get the visibility it deserves, just like any other error in the site.

Tip

Since continuous integration servers like Team Foundation Server or CruiseControl.NET execute MSBuild directly to compile projects, it is very easy to add the MvcBuildViews property to the list of options during your continuous integration builds. If you are using continuous integration to build your ASP.NET MVC websites, I highly recommend you enable this flag during your builds.

Get Programming Razor now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.