Dennis Burton's Develop Using .NET

Change is optional. Survival is not required.
Tags: aspnetmvc | jquery

The model binders in ASP.NET MVC represent a fantastic example of Coding by Convention. If you choose to abide by the naming rules, large amounts of work can be performed for you, but if you stray off of the path, you will have a lot of code to write. Let’s start with the simplest possible case, a model with a single property and a view with a single textbox for populating that property.

public class SimpleModel
  public string SimpleProperty { get; set; }

In order for a Create view to bind to this property, all that is required is the name attribute of any input type (like textbox) match the name of the property in the model you intend to populate, in this case SimpleProperty.

<input name="SimpleProperty" />

When the form is submitted, the Create action on the SimpleModelController is fired with a SimpleModel object as a parameter. This object has the SimpleProperty set with the value from the <input> with the same name. Pretty cool, but where is the massive amount of work being done for me?

Nested Classes

Now, lets make the model class a bit more interesting. This time, the model will be an outer class containing a nested class.

public class OuterClass
  public string SomeProperty { get; set; }
  public NestedClass Nested { get; set; }

public class NestedClass
  public string SomeOtherProperty { get; set; }

Now, if you use the Add View wizard, the resulting view will not contain any reference to the NestedClass, but really who keeps those around anyway? (Until v2 when you can customize the template.) Just as in the previous sample, we need an <input> for each property that should be populated with values from the POST. SomeProperty is the same case as the previous sample; Nested and SomeOtherProperty are slightly different. To understand the convention applied to this binding, think of how you would access SomeOtherProperty from an instance of an OuterClass. You would access the outerClass.Nested.SomeOtherProperty, but remember outerClass is our model and does not need to be named, so we are left with Nested.SomeOtherProperty.

<input name="SomeProperty"/> 
<input name="Nested.SomeOtherProperty" />

Again, when the form is submitted, the Create action on the OuterClassController will have a fully populated OuterClass as a parameter including an instance of NestedClass with SomeOtherProperty set. Now, I am starting to get impressed. The techniques used to map controls to objects within WebForms, are starting to look pretty clunky. The default model binder is a powerful tool as long as you follow the naming rules.

Nested Lists

This next scenario is where it all clicked for me. I was trying to bind a dynamic list of objects to a property of my model. This is a simplified version of the scenario I was looking at:

public class LogEntry 
  public int BloodSugar { get; set; } 
  public List<FoodEntry> FoodEntries { get; set; } 

public class FoodEntry 
  public string Name { get; set; } 
  public string Carbs { get; set; } 

Just as with simple nested property, the default strongly-typed view creator will not add any UI elements for the nested class. That is fine; fewer lines of code for us to delete. To set up the view correctly, think of the description given in the last sample for the naming convention and the Rule of Least Surprise. If you think of how to access each individual element of FoodEntries you would have FoodEntries[N].Name and FoodEntries[N].Carbs. These are the names required of our input elements on the view.

<div class="logEntry"> 
  <div class="bloodSugarEntry"> 
    <label for="BloodSugar">BloodSugar:</label> 
    <input type="text" name="BloodSugar" />
  <div class="foodEntries"> 
    <div class="foodEntry"> 
      <label for="FoodEntries[0].Name">Name:</label>
      <input type="text" name="FoodEntries[0].Name" /> 
      <label for="FoodEntries[0].Carbs">Carbs:</label>
      <input type="text" name="FoodEntries[0].Carbs" /> 
    <input type="button" value="Add More" name="FoodEntries_AddMore" /> 
    <input type="submit" value="Log It!" /> 

Now that there is some structure in the HTML that we can leverage, we can start adding some dynamic client side behavior. In this case, I want to be able to add more food items to the form without having to postback or even perform any AJAX requests. JQuery will be leveraged to inject more food entries into the DOM. The following code is added to the click event from the FoodEntries_AddMore button.

$('#FoodEntries_More').click(function() { 
    var nextFoodEntryIndex = $('.foodEntry').size(); 
    var nextFoodEntryNamespace = 'FoodEntries[' + nextFoodEntryIndex + ']';

    var newFoodEntry = $('<div class="foodEntry">' + 
                         '<label for="' + nextFoodEntryNamespace + '.Name">Name:</label>' + 
                         '<input type="text" id="' + nextFoodEntryNamespace + '_Name"' +
                         'name="' + nextFoodEntryNamespace + '.Name" />' + 
                         '<label for="' + nextFoodEntryNamespace + '.Carbs">Carbs:</label>' + 
                         '<input type="text" id="' + nextFoodEntryNamespace + '_Carbs"' +
                         'name="' + nextFoodEntryNamespace + '.Carbs" />' + 


What this does is look at the set of food entries to determine how many are on the current page in order to determine the index used for the name on the next row of food entries. The first click will result in adding FoodEntries[1].Name and FoodEntries[1].Carbs. These names follow the naming convention that we established earlier. The default model binder recognizes this naming convention and populates the LogEntry object with as many FoodEntry items as have been created on the page.


All of this auto-magic binding assumes that you are willing and able to follow the naming conventions necessary to have the default model binder do the work for you. If you are facing an exceptional situation where the default model binder will not work for you, use a FormCollection in the Create method and do all of the parsing yourself or create a custom model binder. As you can imagine, these tasks can become complicated and unreadable. So if at all possible, try to follow the path that does the work for you. It is worth noting that this is not at all like the drag and drop designers that have a habit of creating poor, hard-to-maintain code. All that is happening here is a form element to object mapping with the code on either end being as elegant as you like.

Where is the code?

Code for this post can be found on GoogleCode at

Please login with either your OpenID above, or your details below.
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview

Dennis Burton

View Dennis Burton's profile on LinkedIn
Follow me on twitter
Rate my presentations
Google Code repository

Community Events

Windows Azure Boot Camp Lansing GiveCamp