Ask Kathleen
Kathleen Dollard Delves into ASP.NET MVC 3
This month's Ask Kathleen column answers your questions about the new Razor view engine in Microsoft's Model-View-Controller framework. Part 1 of 2.
Q: What's new in ASP.MVC 3?
A: Microsoft released the latest version of its Model-View-Controller (MVC) framework in January. The source code for the Web application framework, which is based on ASP.NET and the Microsoft .NET Framework 4, is available under the Microsoft Public License (Ms-PL). ASP.NET MVC 3 offers significant improvements in three key areas: view creation, client-side support and extensibility. Extensibility deserves a column of its own, so I'll focus on the other features in this month's column. ASP.NET MVC 3 can be installed side-by-side with ASP.NET MVC 2.
The MVC view you create inside Visual Studio 2010 (earlier versions are not supported) is a template that's executed by a view engine to create .NET Framework-based code. This code is later run by your server to produce HTML output (see Figure 1). Previous versions of ASP.NET MVC offered a single view engine supporting ASP.NET WebForms style syntax. It isn't ASP.NET WebForms, but it uses the same delimiters and syntax, making initial immersion into writing MVC views easy. The framework also allowed alternate view engines, and several were created within the community.
[Click on image for larger view.] |
Figure 1. The multistep process of creating a view leverages the .NET compiler. |
New View Engine
With ASP.NET MVC 3, Microsoft offers a new view engine called Razor that supports a unique templating language. ASP.NET MVC 3 also introduces the ability to simultaneously run multiple view engines in a single project (Web site). Running multiple view engines is important if you want to run existing ASP.NET WebForms style views alongside Razor views.
Razor views use the new Razor templating language. The core requirement of a templating language is to isolate literal code from expressions that return output and template logic. Most templating languages do this with an open and close construct surrounding either the literal -- or more commonly -- the expressions and code logic. But Razor is not a general-purpose templating language -- it expects C# or Visual Basic syntax for expressions and code logic embedded within HTML. Because it assumes this language context it can imply intent, use a single opening delimiter (the @ sign) and, in many cases, recognize the block close and return to literal output. When Razor can't imply intent, you can explicitly open and close template logic with curly brackets and expressions with parentheses in C# Razor templates. This results in clean syntax.
The opening of the default Index.cshtml from the Web application MVC 3 project (with Razor selected as the view engine) illustrates the two most important constructs:
@{
ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
The first @ indicates a Razor code block. The curly brackets are required so that Razor can distinguish between setting the title of the ViewBag and outputting the title followed by = "Home Page" The expression that outputs the message doesn't require a closing bracket because Razor recognizes the < as a return to HTML. Within the code block, all C# rules apply, including the semicolon line delimiter. When the @ sign is followed by keyword, the curly brackets aren't needed and Razor is able to determine what code to execute, and what to output as literals:
@foreach (var item in Model) {
<tr>
<td>
@item.CustomerNumber
</td>
<td>
@item.Name
</td>
</tr>
}
The Visual Basic version of the default Index templates shows the same expression syntax, but more verbose logic delimiters:
@Code
ViewData("Title") = "Home Page"
End Code
<h2>@ViewData("Message")</h2>
In all cases, Visual Studio helps by highlighting the Razor delimiters in bright yellow and the Razor code with a pale blue background (with standard Visual Studio colors) so you can tell what will be executed and what will output as literals.
Sometimes, Razor inference may not represent your intent for a complex expression. When this occurs, simply surround your expression with parentheses in either C# or Visual Basic. The first line outputs "2 + 2" and the second outputs "4":
@{var x = 2; }
<p>@x + 2</p>
<p>@(x + 2)</p>
<ahref="/Images/@(customerLogo).jpg"></a>
Razor adds a ToString call to any non-string output. The last line illustrates another reason you might need to define the end of your expression: Razor would consider the trailing text (.jpg) part of the expression. You can also use explicit syntax to include C# generics. Razor will otherwise interpret the < as a tag element opening.
Occasionally Razor may interpret content as code. This happens if the content doesn't start with an HTML tag and lies within a code block. You can explicitly switch from code to markup with the @: character sequence:
@foreach (var item in Model)
{
@: Item name: @item.FirstName
}
If you need to include comments or comment-out code, you can use the @*:
@* comment *@
One of the best Razor features is one you may not notice. The team did a great job of determining whether you're working in code or HTML and offering the appropriate context-based IntelliSense.
The Visual Basic and C# templates illustrate two different approaches to ViewData that work in both languages. ViewBag is new and simply a version of ViewData that's of dynamic type. Simultaneous use of the two styles won't confuse MVC, though I doubt your teammates would appreciate it. The ViewBag allows the simpler, non-quoted syntax, although neither version has any type safety or IntelliSense.
While Razor is not a general-purpose generation language, you can use it outside of MVC and can find a good explanation of this usage on Rick Strahl's blog. If your templates are known at compile time, preprocessed text templates are probably easier to use.
Your Razor view is derived from the generic System.Web.Mvc.WebViewPage class; however, you don't need to specify this explicitly in your view. If you specify nothing, your view will derive from the dynamic version of the base class. More often, you'll use the @model directive to indicate the type of your model, which may be either a type for simple data or one of the IEnumerable types for list data:
@model IEnumerable<CustomerModel>
The Razor engine supports layouts. Layouts serve the same purpose as master pages, offering consistent elements and layout. Layouts can be nested and you can explicitly specify them in the view. You can also define initializing behavior for all the views in your site through the _ViewStart.cshtml. Defining the layout in this file applies it across all pages of your project. If some need a different layout, you can set it explicitly in the page to override the ViewStart.cshtml behavior. If you need to skip the layout on a page, simply set the Layout property to null. While the most likely use of _ViewStart will be setting layouts, it can contain any initialization code you want run for all your pages.
Layouts are merged into your page so the resulting HTML displays no artifacts of this creation detail. Within the layout you specify the location of content with the @RenderBody directive. If you'd like to insert different portions of your content in different places, you can insert the @RenderSection directive in your layout, passing the section name. Within your view you define sections with the @Section directive:
@section Summary {
@Html.DisplayFor(Model.FullName);
@* additional content *@
}
This combination of features allows complex layouts that are easy to create and maintain.
Controllers have a couple of improvements in ASP.NET MVC 3. You can apply global action filters, which are especially convenient for error and logging filters. Controllers return action filters and MVC 3 supplies two new action filters: -HTTPNotFoundResult and HTTPStatusCodeResult. Also, the RedirectResult has a Permanent property and the default controller has a new RedirectPermanent method. The permanent redirect issues a 301 redirect, rather than the non-permanent 302 redirect.
Asynchronous JavaScript and XML (AJAX) improvements include automatic model binding to JavaScript Object Notation (JSON) data. This allows incoming JSON parameters to be filled behind the scenes by the default model binder. MVC already had a JSON result to manage outgoing JSON data.
Controllers now support output caching of child actions. Child actions allow you to compose action output from several potentially reusable components. Caching at this granular level can significantly reduce the server load and improve performance in some pages.
The .NET Framework 4 data annotations namespace got a boatload of new data annotation attributes. ASP.NET MVC 3 leverages these attributes, allowing richer declarative validation. These new attributes include DisplayAttribute, UIHintAttribute and EditableAttribute. The default model metadata provider, DataAnnotationsModelMetadataProvider, uses these attributes in creating model metadata, which can be used to make decisions in views and for server-side and client-side validation.
Web development is moving toward the declarative style of HTML5. Instead of requiring JavaScript code in the page to provide the client-side experience, HTML5 offers a standard set of attributes to declare the intended behavior and allow common code libraries to do the real work. This approach of avoiding code in the page is called unobtrusive JavaScript and is supported by MVC 3. Unobtrusive JavaScript makes cleaner, smaller HTML. But the real benefit is the declarative approach that removes library dependencies from your HTML. This allows you to switch JavaScript libraries in the future with relative ease.
The current hot JavaScript library is jQuery. Microsoft adds jQuery validation in ASP.NET MVC 3. The company is contributing to the jQuery library and two significant future plug-ins: one for templating and one for localization.
Be sure to read
Part 2 of this column, where I cover dependency injection and extending parts of the framework.
About the Author
Kathleen is a consultant, author, trainer and speaker. She’s been a Microsoft MVP for 10 years and is an active member of the INETA Speaker’s Bureau where she receives high marks for her talks. She wrote "Code Generation in Microsoft .NET" (Apress) and often speaks at industry conferences and local user groups around the U.S. Kathleen is the founder and principal of GenDotNet and continues to research code generation and metadata as well as leveraging new technologies springing forth in .NET 3.5. Her passion is helping programmers be smarter in how they develop and consume the range of new technologies, but at the end of the day, she’s a coder writing applications just like you. Reach her at [email protected].