Working with the ASP.NET MVC 3 Razor View Engine
Take advantage of MVC 3 Razor's multi-syntax approach to accelerate your coding process
May 24, 2011
Get under the hood of the ASP.NET MVC 3 framework and you’ll find a recently released view engine named Razor. Razor uses a new construct by leveraging C# and VB.NET code within a new type of view that, at first, will seem foreign to ASP.NET developers. At an elementary level, Razor replaces a bunch of <% %> statements with "@" and "@ { }" statements. But here under the hood, there’s a lot more going on.
Razor takes its cue from some of the other popular view engines, such as Spark and NHaml. Razor seeks to add simplicity to the process by combining C# or VB code with a new inline syntax that's flexible yet compact. Scott Guthrie, corporate vice president for the Microsoft .NET developer platform, laid out the following design team goals for the Razor framework:
compact, expressive, and fluid
easy to learn
C# and VB.NET-oriented
IntelliSense supportable
unit testable
We're going to examine the product to determine how it meets these design goals. But before we discuss Razor, I want to reiterate that the concept of MVC has not changed in this latest release. Also unchanged are the constructs and their controllers, the inline project areas, and the models However, these components have been enhanced by some new features, such as dependency injection and new attributes. As an ASP.NET MVC developer, you’ll be comfortable with the back-end features, and you should be able to adapt to the new view constructs fairly quickly.
Setting Up a Project
You can create an ASP.NET MVC 3 Razor website in Visual Studio by using a template similar to the ASP.NET MVC view engine format. Figure 1 shows an example of a new Razor website. (I've modified the default setup somewhat).
Figure 1: Project setup of an ASP.NET MVC 3 Razor website
The MVC web application template has been upgraded to include jQuery UI scripts and styles, validation and MVC integration points (for a richer client-side experience), and a slew of cshtml or vbhtml files, which are the new file formats for Razor views (using the respective languages).
You may notice some differences between this project and one that uses the ASP.NET MVC view engine. For one thing, this project includes a _viewstart.cshtml file that seems to have some special meaning. Additionally, certain files begin with an underscore character. The underscore is a special designation that marks a page as private and not servable to the public. This means that this page cannot be requested directly by the browser. Instead, the page is used for partial views, layout files, and other nonpublic needs.
Project Item Templates
In ASP.NET MVC 3, new Visual Studio project item templates now appear under the MVC 3 category in the Add New Item window. Figure 2 shows the following options for ASP.NET MVC 3:
MVC 3 Layout Page: the layout for MVC 3 pages, similar to a master page
MVC 3 Partial Page: a partial page or view for MVC 3
MVC 3 View Page: a view for MVC 3 that doesn't use a Layout page
MVC 3 View Page With Layout: a view for MVC 3, which uses a layout page
In Figure 2, note the indication (in parentheses) of the ASPX or Razor templates.
Figure 2: MVC 3 templates in a Visual Studio project
Syntax
The Razor view engine, similar to the ASP.NET MVC view engine, mixes server-side logic with client-side HTML/JavaScript and now uses the at sign (@) to identify its code segments. The Razor view engine inspects the body of a view for statements that typically begin with @. The engine parses the content to determine whether it's pure content or happens to be server-side code. For instance, if the engine finds the statement "@Model.Name," it interprets this as server-side code. However, "[email protected]" renders as client-side content because of the syntax.
The Razor engine parser is pretty intelligent. It knows what you are typing, and it knows whether the @ sign is identifying server-side or client-side content. Any identified server-side code uses a special yellow highlighting to highlight the @ and gives the code a gray background. However, when you type an email address, this is interpreted correctly as client-side HTML. As soon as the parser interprets characters such as a space, closing HTML tags, or quotation marks to end an HTML attribute, the parser stops highlighting the text and begins interpreting the content as client-side code.
Interestingly enough, this approach works well. We rarely run into situations in which spaces are injected into code (except in a string, which is specifically identified by quotation marks). Also, only a few special characters are allowed by the C# or VB.NET compiler.
It's easy to play around with the view engine to figure out which combinations of content are supported. The following list of scenarios indicates how the view engine interprets the content in each case:
@Model.State: interpreted as server-side code
[email protected]: interpreted as client-side code
t @Model.State: interpreted as a mix of client and server content
@Model.State State: interpreted as a mix of client and server content
Test: correctly renders server-side content into the href attribute of the hyperlink
Test: correctly mixes the state server-side value into the client-side href attribute of the hyperlink
In some cases, you may have to force the parser to recognize pure server-side content. Razor may incorrectly parse certain constructs as client-side content. For instance, what if we wanted to use generics? The parsing engine recognizes generics as HTML content. This interpretation wreaks havoc, so this is where some new constructs come into play. If the code can fit into a single statement, use @ together with parenthesis, as in the following:
@( )
The code within the parenthesis is recognized as pure server-side content.
Sometimes, we must have a larger code block made available to us. Curly brackets denote a code block or segment within Razor. But code blocks are not purely server-side content; they can be mixed. Note the differences between the two syntactic examples in Figure 3.
State: @((Model.State == "FL") ? "33426" : "11111")@{if (Model.State == "FL") {33426}else {11111}}
Code blocks can mix client-side content, but this requires the use of either an HTML element or the new element. The element doesn't actually render anything; instead, it's a clear and concise way to identify client-side text. This element is required because Razor can't accurately determine the transition from a server-side if statement to client-side content. Any HTML tag or the tag is a clear demarcation of this transition.
Code Structures
The Razor view engine supports most, if not all, code structures in your favorite .NET language. It uses the inline or code block approach. The example in Figure 4 illustrates some of the various code structures in C#.
@foreach (var item in Model.Items){}NameValueIs [email protected]@((item.Value != null) ? item.Value : null)@switch (item.IsActive){case true:Activebreak;case false:Inactivedefault:Unknownbreak;}
Razor also supports if/else statements, While loops, Do...While loops, and all other code structures available to developers in C# or in VB.
View Headers
Headers in a Razor implementation also differ from the standard @Page header used in ASPX pages. In ASP.NET Web Forms or MVC views, the header contains all the common settings defined for the page. The settings can also be set in code.
The Razor view engine doesn't use quite the same approach. Instead, it uses a code block to define these common page settings, typically at the top of the page. Figure 4 compares the differences between the two implementations.
Notice that we don't have to specify the base class or the development language. This is especially true of the latter because the page extension itself has C# in the name! In Razor, there is no explicit title property. However, the title element that's defined in the master page references the same ViewBag.Title expression that's defined in Figure 5. The ViewBag dynamic object is a dynamic version of the ViewData dictionary—an easy way to pass properties from a controller or from a view to the view or to the layout page.
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml";}
For custom base classes, the earliest versions of ASP.NET MVC 3 used an @inherits statement. In later releases, the @model keyword was supported as a more concise way of defining a model. This keyword also let developers avoid having to explicitly define the base page over and over again. Instead of this definition, we can make use of a new configuration section to set up the base page class for all our view pages. This section is shown in Figure 6.
Helpers
The Razor view engine has a lot of utilities in its arsenal. Among its potent weapons are helpers, which provide another way to create reusability within an application. A helper is defined within the view and provides a reusable snippet within the application. This feature resembles ASP.NET Web Forms that have user controls. However, the syntax is more concise and compact, and the logic fits together into one package. You can see these differences in the example in Figure 7.
@helper SomeHelper(string text){if (text != null){@text}else{Unknown}}..
Helper with Text:
@SomeHelper("This is not null text.")Helper without Text:@SomeHelper(null)
In a view, helper methods can be defined within the markup that uses the helper. With the helper methods in place, a helper invokes itself as a function call, just like any other method. This creates a reusable set of logic that can easily be called repeatedly. This functionality is reminiscent of JavaScript and of how easily JavaScript can create components or HTML elements on the fly.
According to both the Razor online documentation (see The Razor View Engine section on the ASP.NET MVC 3 web page) and Scott Guthrie's blog, global helpers (available throughout the entire application) are supported by placing a page in the ~/Views/Helpers folder. Any helpers that are defined in this folder should be available in any part of the application. However, because of time constraints, this feature wasn't implemented. The workaround is first to create an App_Code folder, then add to the folder a new helper created as a .cshtml or .vbhtml file. You can access the new helper by using a . convention. The example in the sample code uses Helpers.GlobalHelper().
Functions
Whereas helper methods create reusable blocks of UI logic, a function provides pure server-side methods that can be reused within the view. For developers who are used to ASP.NET Web Forms and code-behind pages, functions are very similar to such methods in that they create an API to the view that has properties or methods. For instance, Figure 8 shows a sample functions block that creates a Date property and two string-rendering methods.
@functions {private DateTime _executionDate = DateTime.Now;public DateTime ExecutionDate{get { return _executionDate; }set { _executionDate = value; }}public IHtmlString GetFunctionMessage(){return new HtmlString("This is my function message.");}}
Execution Date:
@ExecutionDateFunction:@GetFunctionMessage()
This sample shows that we can define properties by using variables to persist the data, or by using methods to perform string rendering. Razor is consistent with ASP.NET MVC in that it, too, uses the IHtmlString interface for rendering markup content. The newly created functions exist for the view only because the functions block is typically defined at the top of the view.
Inline Templates
Certain types of extensions can support inline templates, such as a grid's item template. These extension types can be part of the ASP.NET MVC framework or can even come from third-party frameworks. These inline templates are neatly supported in Razor. Razor also uses the @ to mark the notation of a template, as illustrated in the example in Figure 9.
//Defining the templatepublic IHtmlString RenderInlineTemplate(Func template){return new HtmlString("" + template(Model) + "");}Function with Template:@RenderInlineTemplate(@@string.Concat(item.Name, " (", item.City, ", ", item.State, ")"))
A template here is represented by a Func statement; this lambda expression defines one input and output. Usually, templates such as this use the C# lambda syntax, which uses an equal sign (=) and a greater than sign (>). However, Razor templates begin with an @, followed by client HTML that has server-side code inside the template, which is also denoted by the @. This may be a little confusing, but the Razor view engine neatly recognizes these client markup-to-server code changes.
However, this neglects one important point: How do we know what data is being used within the template? After all, by using a lambda expression, the consumer knows what the alias for the lambda is. Razor statically defines the name of the lambda reference as "item," as denoted in the example in Figure 9. The template function helper that I created accepts a model reference. Therefore, "item" points to the reference that's defined as the first parameter of the lambda.
Layout Pages (aka Master Pages)
The concept of a Razor Layout page is very similar to that of an ASP.NET MVC or Web Forms master page. The layout page defines a template for view pages that implement that layout. Typically, the layout page contains the header and core body of the entire site. The page has predefined placeholders within itself to specify areas that can accept content that's inserted by the view page. You can see the use of some new methods in the layout page sample in Figure 10.
@RenderPage("~/Views/Shared/_Header.cshtml") @Html.ActionLink("Home", "Index", "Home") @Html.ActionLink("About", "About", "Home") @RenderBody() @RenderSection("Footer", false)
The @RenderBody and @RenderSection methods have a special purpose in regards to the Razor view engine. The @RenderSection helper is the equivalent of the ContentPlaceHolder controls in an ASP.NET master page; it defines a region of the layout page that content will be inserted into by the consuming view. The @RenderBody method acts a little differently in that it's the default placeholder for the view's entire content. The @RenderBody method renders all the content of the view, whereas special designations that are called sections can replace the named sections that are defined here. This works differently than ASP.NET Web Forms because of the Razor syntax. In Razor, any @ declarations outside the main view body are denoted as special and are not rendered as the body of the view. This is the reason that @RenderSection statements can also be defined within the same view.
Sections
Sections are similar to a content control in ASP.NET Web Forms. They override the Razor equivalent of the ContentPlaceHolder control to insert custom UI content. Remember the @RenderSection method that was defined in the layout page example? The following example inserts its content into the section of the given name that tries to render itself in the location in which the RenderSection method has the same name. This action is similar to that of a ContentPlaceHolder control in an ASP.NET Web Form. The section that has the given name defines the inline HTML to place to render the content.
@section Footer {This is a footer.}
It's important to note that some sections are required, whereas some are optional. An override to the RenderSections helper lets you define additional settings, one of which is the optionality of a section.
ViewStart
If you've ever opened up an ASP.NET MVC 3 Razor site, you may have seen the _ViewStart.cshtml page. This is a pretty slim file, as shown in the following example:
@{ Layout = "~/Views/Shared/_Layout.cshtml";}
The ViewStart page was intended to serve as a central starting point for all views and to contain the common settings across all views. However, there are limitations as to what can happen when a view starts. For instance, suppose you want to add a common helper or define an implementation for a section that will appear throughout the entire application. This seems like a logical place to put these pieces of code that can be used globally throughout an application. However, implementing a section here throws an exception and is not supported. In addition, the example is able to define an @helper or @functions block, but I was unable to consume the block in a view. So the block can be defined but it can't be used.
Helper Method Conventions
The ASP.NET MVC view engine employed a series of helper methods to reduce the overall amount of HTML that a developer had to write. For instance, instead of having to manually render a hyperlink tag, the Html.ActionLink helper provided a shortcut for this feature. Other helper methods render common HTML or even complex controls.
Razor supports all of the existing server-side components and extensions that are already written in ASP.NET MVC. But Razor, by nature, has issues regarding certain constructs, such as generics. A generic definition in C# is very similar to an HTML tag, and the Razor parser incorrectly marks a generic as client-side code.
To make Razor work correctly with generics, add parentheses the entire code segment that follows the @, as follows:
@(Html.TextBoxFor(i => i.Name))
Using parentheses around the code marks the segment as pure server-side code, and it correctly marks certain constructs.
Framework
The Razor view engine does not leverage some of the core components of the ASP.NET framework. This means that the Razor framework includes some new DLLs that are specific to the framework. While the view components reside mostly within the new 3.0 version of the System.Web.Mvc component, the System.Web.WebPages and System.Web.Razor DLLs contain essential integration components.
The Razor framework comes with its own view engine class and view base classes, among other components. The base view components still leverage all the same framework components of the ASP.NET MVC framework in that the Razor framework maintains a relationship to the HTTP and view contexts, to the HTML/AJAX helpers, to the routing components, and to the model/state bags.
Something Borrowed, Something New
Razor is not something to worry about. This framework doesn't replace something that already exists. Razor attempts to incorporate the core components of ASP.NET MVC while also including a brand-new way of developing the UI to incorporate some of the great features from other frameworks—even including third-party, open source frameworks.
By taking advantage of Razor helpers and functions, you can easily reuse snippets of code or markup within a view, or global helpers can reuse markup throughout an entire application. The starting view page contains the common settings for each and every view, and the layout page provides a consistent look and feel for an entire application. The layout page contains a list of sections that can be overridden in a page. Finally, Razor supports full C# syntax and everything that MVC 3 has to offer.
About the Author
You May Also Like