Model binding plays a central, yet nearly invisible, role in any ASP.NET MVC application. It is what allows input data on an HTML form to be mapped to server-side variables.

Take a look at this markup:

<div>
    @using (Html.BeginForm("NoBindingPost", "Home"))
    {
        <div>
            ID:
            <input name="ID" type="text" />
        </div>
        <div>
            First Name:
            <input name="FirstName" type="text" />
        </div>
        <div>
            Last Name:
            <input name="LastName" type="text" />
        </div>
        <div>
            <input type="submit" value="Go!" />
        </div>
    }
</div>

When the values in those fields get posted to the server, they get collected into the Request collection, which can be accessed like so (using the FlashMessage from an earlier post)

[HttpPost]
public ActionResult NoBindingPost()
{
    string id = Request["Id"];
    string firstName = Request["FirstName"];
    string lastName = Request["LastName"];
    SetFlash(FlashMessageType.Success, "User saved!", firstName + " " + lastName + ", ID: " + id + " saved!");
    return RedirectToAction("Index");
}

But the binding is more complex, and thorough, than that. For example, using the same HTML, we can pass inputs to the action:

public ActionResult QueryStringBinding(string ID, string firstName, string lastName)
{
    SetFlash(FlashMessageType.Success, 
             "User saved!", 
             firstName + " " + lastName + ", ID: " + ID + " saved!");
    return RedirectToAction("Index");
}

If we're feeling really frisky, we can have an entire class with those properties in it:

public class Employee
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

And the model binding will map those values into an instance of the Employee class. (Note that in order for this to work, the class being passed into the action must have a constructor that takes no inputs.)

[HttpPost]
public ActionResult ClassBinding(Employee employee)
{
    SetFlash(FlashMessageType.Success, 
             "User saved!", 
             employee.FirstName + " " + employee.LastName 
                 + ", ID: " + employee.ID + " at building " 
                 + employee.WorkLocation.BuildingName + " saved!");
    return RedirectToAction("Index");
}

Let's make this even more complex: We'll add a new WorkLocation property to the Employee class:

public class WorkLocation
{
    public string BuildingName { get; set; }
    public string Description { get; set; }
}

public class Employee
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public WorkLocation WorkLocation { get; set; }
}

And now we can modify the markup so we can bind some text boxes to the new WorkLocation property:

@using (Html.BeginForm("ClassBinding", "Home"))
{
    <div>
        ID:
        <input name="ID" type="text" />
    </div>
    <div>
        First Name:
        <input name="FirstName" type="text" />
    </div>
    <div>
        Last Name:
        <input name="LastName" type="text" />
    </div>
    <div>
        Building Name:
        <input name="WorkLocation.BuildingName" type="text"/>
    </div>
    <div>
        Building Description:
        <input name="WorkLocation.Description" type="text"/>
    </div>
    <div>
        <input type="submit" value="Go!" />
    </div>
}

And the POST looks just like you think it does:

[HttpPost]
public ActionResult ClassBinding(Employee employee)
{
    SetFlash(FlashMessageType.Success, 
             "User saved!", 
             employee.FirstName + " " + employee.LastName + ", ID: " 
                 + employee.ID + " at building " 
                 + employee.WorkLocation.BuildingName + " saved!");
    return RedirectToAction("Index");
}

The point here is that all of it just works. It's predictable, and therefore reproducible, and that's what makes it so powerful.

Let's add one more layer. Say we want to have a list of Employees in the action:

[HttpPost]
public ActionResult ListBinding(List<Employee> employees)
{
    string namesList = "";
    foreach(var emp in employees)
    {
        namesList += emp.FirstName + " ";
    }
    SetFlash(FlashMessageType.Success, "Employees saved!", "First names: " + namesList);
    return RedirectToAction("Index");
}

The model binding on the form uses array syntax to designate a list of objects, like so:

@using (Html.BeginForm("ListBinding", "Home"))
{
    <div>
        Employee 1 ID:
        <input name="[0].ID" type="text" />
    </div>
    <div>
        Employee 1 First Name:
        <input name="[0].FirstName" type="text" />
    </div>
    <div>
        Employee 1 Last Name:
        <input name="[0].LastName" type="text" />
    </div>
    <div>
        Employee 1 Building Name:
        <input name="[0].WorkLocation.BuildingName" type="text" />
    </div>
    <div>
        Employee 1 Building Description:
        <input name="[0].WorkLocation.Description" type="text" />
    </div>
    <br/><br/>
    <div>
        Employee 2 ID:
        <input name="[1].ID" type="text" />
    </div>
    <div>
        Employee 2 First Name:
        <input name="[1].FirstName" type="text" />
    </div>
    <div>
        Employee 2 Last Name:
        <input name="[1].LastName" type="text" />
    </div>
    <div>
        Employee 2 Building Name:
        <input name="[1].WorkLocation.BuildingName" type="text" />
    </div>
    <div>
        Employee 2 Building Description:
        <input name="[1].WorkLocation.Description" type="text" />
    </div>
    <br /><br />
    <div>
        Employee 3 ID:
        <input name="[2].ID" type="text" />
    </div>
    <div>
        Employee 3 First Name:
        <input name="[2].FirstName" type="text" />
    </div>
    <div>
        Employee 3 Last Name:
        <input name="[2].LastName" type="text" />
    </div>
    <div>
        Employee 3 Building Name:
        <input name="[2].WorkLocation.BuildingName" type="text" />
    </div>
    <div>
        Employee 3 Building Description:
        <input name="[2].WorkLocation.Description" type="text" />
    </div>
        <input type="submit" value="Go"/>
}

As always, you can grab the a working sample project from GitHub. Feel free to submit a pull request!

exceptionnotfound/ModelBindingDemo
Contribute to exceptionnotfound/ModelBindingDemo development by creating an account on GitHub.

Happy Coding!