New Templated Helpers Improve SoC

 

SoC (from WikiPedia)
In my MSDN article Walkthrough: Using Templated Helpers to Display Data I show how to annotate your data model to specify that a field should be rendered with a type-specific control.  The download sample code also includes a control using the new Html.EditorFor helper. With one line of code you can open an editor on your strongly typed object. Using the line below, my edit/create view can instantiate an editor on the Product object.

 <%= Html.EditorFor(Product=> Model)%> 

The automatic scaffolding works great for POCO objects; with EF classes the additional EDM properties are added and also scaffolded. The image below shows some of the fields of the Product table in the AdventureWorksLT sample database.

 

 

EFartifacts

We really don’t want to be displaying IsFixedSize  (changing the check box state won’t persist the metadata anyway), Length, Rank and other EDM metadata that are not fields in the Product table. Analogous to the DisplayTemplates directory I talk about in my MSDN article, you create a Product.ascx control in the Views\Home\EditorTemplates directory. The

 <%= Html.EditorFor(Product=> Model)%>

line will automatically use the Views\Home\EditorTemplates\Product.ascx template.  Additionally, the same line of code works in both the edit and create views. The snippet below shows a Product template. (Note: I omitted most of the fields in the snippet below, the download is more complete.)

 <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcTmpHlprs.Models.Product>" %>

<% using (Html.BeginForm()) { %>
    <fieldset>
<div><%= Html.LabelFor(c => Model.Name)%> 
    <%= Html.EditorFor(c => Model.Name)%>
    <%= Html.ValidationMessage("Name", "*") %>

   <%= Html.LabelFor(c => Model.StandardCost)%>
 <%= Html.EditorFor(c => Model.StandardCost)%>
 <%= Html.ValidationMessage("StandardCost", "*")%>
 </div><div>
 </div><div>
   <%= Html.LabelFor(c => Model.ListPrice)%>
 <%= Html.EditorFor(c => Model.ListPrice)%>
 <%= Html.ValidationMessage("ListPrice", "*")%>
 </div><div>
 
  <p> <input type="submit" value="Save"/> </p>
    </fieldset>
    
<% } %>

Error Messages and Debugging

DataAnnotations are your first line of defense for validating input. See my related DataAnnotations blog and my MSDN article How to: Validate Model Data Using DataAnnotations Attributes. The sample code shows how well DataAnnotations integrate with the new templated editor.

For security reasons, you generally don’t want to catch and display an exception. If two users try to add “Widget 99” to the Products table, the second submit will fail because the product name has unique key constraint. The download sample shows the error message “SqlException: Violation of UNIQUE KEY constraint 'AK_Product_Name' . Cannot insert duplicate key in object 'SalesLT.Product'.” I consider that an excellent error message for a programmer, but it’s not friendly to the end user.  One approach is to catch common exceptions like this, parse the message and try to give the user a hint.

Model errors mask the internal error message for security reasons. The sample has the following code to help you figure out the problem in a debug/development environment (For security reasons, don’t use this for production code.)

 foreach (var modelState in ModelState.Values) {
    foreach (var error in modelState.Errors) {
        if (error.Exception != null) {
            throw modelState.Errors[0].Exception;
        }
    }
}

Remove the comments surrounding the following line in the Products.ascx template to test the model state error debugging code.

 <%= Html.Hidden("ProductID", Model.ProductID)%>

You get the following helpful message (at least for a developer) on where the problem occurred.

The property 'ProductID' is part of the object's key information and cannot be modified.

Comments

  • Anonymous
    August 20, 2010
    This code looks a bit strange.  Do you actually mean this instead? foreach (var modelState in ModelState.Values) {    foreach (var error in modelState.Errors) {        if (error.Exception != null) {            // throw first non-null exception            throw error.Exception;        }    } }

  • Anonymous
    September 08, 2010
    Hi I am very interested in learning MVC and trying to work through a tutorial but can't get past this <%: Html.EditorFor(model => model.Album, new { Artists = Model.Artists, Genres = Model.Genres}) %> I am a VB developer and cannot find the VB equivalent of '=>' I have looked at the MSDN documentation and found the VB syntax Public Shared Function EditorFor(Of TModel, TValue) ( _ html As HtmlHelper(Of TModel), _ expression As BLOCKED EXPRESSION As MvcHtmlString but after several hours of trying various combinations I cannot work out how to code this. Hope you can help

  • Anonymous
    October 10, 2010
    Brian, you should use  Html.EditorFor(Function(c) { c.Album; } @all: Is there a Html UI Helper Caching active in mvc?!