October 2010 - Posts - Jon Galloway

October 2010 - Posts

ASP.NET MVC 3 Beta: Some FAQ’s

Where do I look to find the features / install / release notes / etc.?

In the past, you needed to look up the announcement posts (especially Scott Guthrie’s, Phil Haack’s, and Scott Hanselman’s). Not that there’s anything wrong with that… but it’s a bit random. So I worked with Phil to set up a dedicated MVC 3 page on the http://asp.net site: http://asp.net/mvc/mvc3

It’s got a top list of features, install links (both the WebPI link and the installer), a link to the Release notes (in glorious HTML format for a change), and links to some of the top blog posts on new features in MVC 3. We’ll update this page with top info on MVC 3 when it ships.

MVC3 Web Page

Release Notes in HTML format?

Crazy, huh? Seems like this HTML thing might just catch on, so in addition to the Word doc release notes (still available here) you can browse the release notes online. We’ll update this when MVC 3 ships.

Is it safe to install this? It’s a Beta, will this destroy my computer and affect my production code?

This is a side-by-side install with MVC 2 as well as Visual Studio 2010 and the rest of the ASP.NET 4 stack. That means that you can install and uninstall MVC 3 without any impact on your existing MVC 2 projects, Visual Studio 2010, etc.

MVC Side-by-side installs

I’ve installed and uninstalled MVC 3 Preview and pre-release copies of the Beta countless times – it’s a quick, clean install and uninstall.

Can I deploy MVC 3 Beta applications to production?

Yes, MVC 3 Beta includes a go-live license.

What’s the relationship between ASP.NET MVC 3 and NuPack?

The MVC 3 installer includes the Visual Studio tooling for NuPack. When you right-click on your project references, you’ll have a new “Add Package Reference” option.

NuPack - Add Package Reference

You can also get to it from View / Other Windows / Package Manager Console.

View - Other Windows - Package Manager Console

NuPack is a great way to speed up and simplify package management in your ASP.NET MVC applications, but the only real direct link between ASP.NET MVC and NuPack is that there’s a NuPack package for ASP.NET MVC 3 that generates scaffold views:

PM> nap MvcScaffold
Attempting to install 'MvcScaffold 1.0'...
Successfully installed 'MvcScaffold 1.0'

PM> Scaffold-MvcViews -ModelType Product
Added file 'Views\Product\List.aspx'
Added file 'Views\Product\Details.aspx'
Added file 'Views\Product\Edit.aspx'
Added file 'Views\Product\Create.aspx'
Added file 'Views\Product\Delete.aspx'

You can install the NuPack VSIX directly without installing ASP.NET MVC 3, and you can use ASP.NET MVC 3 without NuPack. If you like to work harder than you need to.

Read more about NuPack on Phil Haack and Scott Hanselman’s posts. NuPack is a really big deal – both as a great package management system and as an open source project developed by Microsoft and the community.

What’s the relationship between ASP.NET MVC 3 and WebMatrix?

ASP.NET MVC 3 has an install dependency on ASP.NET Web Pages, which is the lightweight web framework which also ships with the WebMatrix tool. ASP.NET Web Pages uses the Razor templating engine, which is also the new default view engine for ASP.NET MVC 3.

David Ebbo gave a great overview of how WebMatrix, Razor, and ASP.NET Web Pages fit together back when the first beta of WebMatrix shipped, which gives some good background information.

So with that in mind, MVC 3 has dependencies System.Web.WebPages for the Razor engine. MVC 3 also includes support for new Web Pages Helper Methods, including Chart, Crypto, WebGrid, WebImage, and WebMail.

Just the addition of a grid is huge – that’s one of the most common questions I get asked by new MVC developers. There have been (and of course still are) other great grid solutions for ASP.NET MVC, including MvcContrib, jqGrid, etc., but it’s nice to have something in the product. You can learn more about the ASP.NET Web Pages helpers here.

What’s the deal with this Service Location thing?

Quoth Brad Wilson:

One of the new features in ASP.NET MVC 3 is the ability to register a service locator that will be used by the framework. Prior versions of the MVC framework have offered opportunities for introducing concepts like service location and dependency injection (DI); in MVC 3, we have formalized the process and opened up several new opportunities for developers.

I’m not going to try to keep up with Brad on this – if you’re at all interested in the Service Location features in MVC 3 (and the changes since the MVC 3 Preview), see his eleven part series on ASP.NET MVC 3 Service Location.

What is this Unobtrusive JavaScript and HTML 5 thing? Does it work on older browsers, too?

Unobtrusive JavaScript is an informally defined approach to the incorporation of JavaScript into HTML which keeps them as separate as possible. This has evolved over time. The original, old-school approach was to wire up event handlers from elements, like this:

<input type="text" name="date" onchange="validateDate()" />

That’s ugly for a lot of reasons – it makes the HTML and JavaScript hard to change without interfering with each other, it  adds non-semantic logic gunk to your HTML, etc.

The next evolution of unobtrusive JavaScript was to wire up handlers from your JavaScript code (rather than from the HTML event attributes). This was the approach used in ASP.NET MVC 2, and I always thought it was generally pretty clean. The client-side validation code wrote out a JSON block which had all the validation rules, and a JavaScript include read the JSON and enforced the validation rules. Look, isn’t the JSON awesome?

<script type="text/javascript">
//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push({"Fields":[{"FieldName":"FirstName","ReplaceValidationMessageContents":true,"ValidationMessageId":"FirstName_validationMessage","ValidationRules":[{"ErrorMessage":"The field First Name must be a string with a maximum length of 160.","ValidationParameters":{"minimumLength":0,"maximumLength":160},"ValidationType":"stringLength"},{"ErrorMessage":"First Name is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"LastName","ReplaceValidationMessageContents":true,"ValidationMessageId":"LastName_validationMessage","ValidationRules":[{"ErrorMessage":"The field Last Name must be a string with a maximum length of 160.","ValidationParameters":{"minimumLength":0,"maximumLength":160},"ValidationType":"stringLength"},{"ErrorMessage":"Last Name is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Address","ReplaceValidationMessageContents":true,"ValidationMessageId":"Address_validationMessage","ValidationRules":[{"ErrorMessage":"The field Address must be a string with a maximum length of 70.","ValidationParameters":{"minimumLength":0,"maximumLength":70},"ValidationType":"stringLength"},{"ErrorMessage":"Address is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"City","ReplaceValidationMessageContents":true,"ValidationMessageId":"City_validationMessage","ValidationRules":[{"ErrorMessage":"The field City must be a string with a maximum length of 40.","ValidationParameters":{"minimumLength":0,"maximumLength":40},"ValidationType":"stringLength"},{"ErrorMessage":"City is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"State","ReplaceValidationMessageContents":true,"ValidationMessageId":"State_validationMessage","ValidationRules":[{"ErrorMessage":"State is required","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"The field State must be a string with a maximum length of 40.","ValidationParameters":{"minimumLength":0,"maximumLength":40},"ValidationType":"stringLength"}]},{"FieldName":"PostalCode","ReplaceValidationMessageContents":true,"ValidationMessageId":"PostalCode_validationMessage","ValidationRules":[{"ErrorMessage":"The field Postal Code must be a string with a maximum length of 10.","ValidationParameters":{"minimumLength":0,"maximumLength":10},"ValidationType":"stringLength"},{"ErrorMessage":"Postal Code is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Country","ReplaceValidationMessageContents":true,"ValidationMessageId":"Country_validationMessage","ValidationRules":[{"ErrorMessage":"The field Country must be a string with a maximum length of 40.","ValidationParameters":{"minimumLength":0,"maximumLength":40},"ValidationType":"stringLength"},{"ErrorMessage":"Country is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Phone","ReplaceValidationMessageContents":true,"ValidationMessageId":"Phone_validationMessage","ValidationRules":[{"ErrorMessage":"The field Phone must be a string with a maximum length of 24.","ValidationParameters":{"minimumLength":0,"maximumLength":24},"ValidationType":"stringLength"},{"ErrorMessage":"Phone is required","ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Email","ReplaceValidationMessageContents":true,"ValidationMessageId":"Email_validationMessage","ValidationRules":[{"ErrorMessage":"Email Address is required","ValidationParameters":{},"ValidationType":"required"},{"ErrorMessage":"Email is is not valid.","ValidationParameters":{"pattern":"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"},"ValidationType":"regularExpression"}]}],"FormId":"form0","ReplaceValidationSummary":false});
//]]>
</script>

Well, I guess it is a little verbose. That’s one of the nice benefits of the MVC 3 unobtrusive validation system – since the validation rules are included as data-* attributes on the elements, the context removes a lot of the need for duplicated information. You can see the unobtrusive validation in action pretty easily by setting the ClientValidationEnabled flag to true in web.config as follows:

<appSettings>
  <add key="enableSimpleMembership" value="false" />
  <add key="ClientValidationEnabled" value="true"/> 
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

Now I’ll add three script references to the bottom of the default Views/Account/LogOn.cshtml (I could also add them to a master page):

@model Mvc3Sample.Models.LogOnModel

@{
    View.Title = "Log On";
}

<h2>Log On</h2>
<p>
    Please enter your username and password. @Html.ActionLink("Register", "Register") if you don't have an account.
</p>
@Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")

@using (Html.BeginForm()) {
    <div>
        <fieldset>
            <legend>Account Information</legend>

            <div class="editor-label">
                @Html.LabelFor(m => m.UserName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(m => m.UserName)
                @Html.ValidationMessageFor(m => m.UserName)
            </div>

            <div class="editor-label">
                @Html.LabelFor(m => m.Password)
            </div>
            <div class="editor-field">
                @Html.PasswordFor(m => m.Password)
                @Html.ValidationMessageFor(m => m.Password)
            </div>

            <div class="editor-label">
                @Html.CheckBoxFor(m => m.RememberMe)
                @Html.LabelFor(m => m.RememberMe)
            </div>

            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
}
<script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>

When the page is displayed, we’ve got Ajax validation with very minimal impact to our HTML source:

<form action="/Account/LogOn" method="post">    <div>
        <fieldset>
            <legend>Account Information</legend>

            <div class="editor-label">
                <label for="UserName">User name</label>
            </div>
            <div class="editor-field">
                <input data-val="true" data-val-required="The User name field is required." id="UserName" name="UserName" type="text" value="" />
                <span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true"></span>
            </div>

            <div class="editor-label">
                <label for="Password">Password</label>
            </div>
            <div class="editor-field">
                <input data-val="true" data-val-required="The Password field is required." id="Password" name="Password" type="password" />
                <span class="field-validation-valid" data-valmsg-for="Password" data-valmsg-replace="true"></span>
            </div>

            <div class="editor-label">
                <input data-val="true" data-val-required="The Remember me? field is required." id="RememberMe" name="RememberMe" type="checkbox" value="true" /><input name="RememberMe" type="hidden" value="false" />
                <label for="RememberMe">Remember me?</label>
            </div>

            <p>
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
</form><script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="../../Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>

See Brad’s posts for more information on both Unobtrusive Client Validation and Unobtrusive Ajax.

You didn’t answer my question. Does this require an HTML5 browser?

This of course raises fun questions, like “what exactly is an HTML5 browser” but we’ll pass on that one for now…

The short answer is that while this leverages data-* attributes which are included in the HTML5 spec, there’s nothing really browser specific about this other than that you’re using a doctype which doesn’t prevent attributes it doesn’t recognize, which is one of the nice benefits of the HTML5 doctype. So, yes, this will work on legacy browsers.

Posted by Jon Galloway | 1 comment(s)
Filed under:
More Posts