Steve Smith's Blog

Musings on Software and the Developer Community

Binding in ASP.NET MVC

Question!

At my ASP.NET MVC + SOLID Principles talk in Cleveland last night, I had a couple of questions about binding in ASP.NET MVC.  For instance:

  • Can you still do something like <%= Bind(“Foo”) %> in your form?
  • How does the controller that receives a POST get back the Model that was used in the form that was posted? 
  • Does it have to be serializable?
  • What if I need to bind custom types to my Model class?
  • Doesn’t having the Model referenced in the View eliminate the need for the Controller?

I answered these last night but I thought they were common enough as a theme that they deserved their own answers here as well.

Does ASP.NET MVC Support Two Way Binding via the Bind() Syntax?

No, not directly.  Rather, you can use HTML Helpers to create your UI (if you like – you can also just hand code the HTML if that’s your preference – ASP.NET MVC is all about you being in control), and then use either the built-in or a custom ModelBinder to convert the POSTed data into your class/Model.  More on ModelBinders below.  The HTML helper syntax might look like this:

 

<%= Html.TextBox("Title", Model.Title) %>

If you don’t like the “magic string” used in this, you might look at Eric Hexter’s Input Builders project.

 

How does the controller that handles the POST get back the Model?

In my example last night, I had some code that looked like this (except it used a blog not a customer):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Customer customer)
{
   // work with Customer here
}
This kind of looks like it’s using Magic, because nowhere am I having to deal with Request.Form[“CustomerName”] or CustomerTextBox.CustomerName as I would typically need to do.  It raises the question for some as to whether there is some kind of passing around of the serialized model (Customer) between the client and the server.  In fact this is not the case.  All that is happening here is standard HTTP, doing a POST, with the fields that were specified in the <form> on the client.  If our form had an <intput type=”text” name=”CustomerName” /> then the POST would include the CustomerName field and whatever value was stored in the TextBox.
 
The “magic” takes place in the form of a ModelBinder.  ModelBinders are responsible for the fairly common task of converting between a collection of Form values and a strongly typed Model class that should be created from these values.  First, let’s look at the equivalent code that doesn’t use a ModelBinder.  If I wanted to create my customer object more explicitly in the controller, I could do it like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection formValues)
{
  var customer = new Customer();
  customer.FirstName = Request.Form["FirstName"];
  customer.LastName = Request.Form["LastName"];
}

This gets old fast.  A better method that is one step closer to the ModelBinder approach would be:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection formValues)
{
  var customer = new Customer();
  UpdateModel(customer);
}

This helper method automatically updates the properties of the object we pass into it with the values from incoming form parameters (using reflection to match up the property names with the form names).  If we want to avoid dealing directly with the FormCollection in our controller, we can simply use the original signature I showed above, and have the controller take in as its parameter the model object (Customer in this case) that we want to have populated from the form.

If there are problems with the population of the model object, the ModelState.IsValid property will be false, and the ModelState will include details about the rule violations (such as a type mismatch).  You can find more detail on this here.

Does the Model used for a controller that handles a POST need to be serializable?

No, it simply needs to have properties that can be mapped to the fields coming in from the POST.  The class itself is never serialized or deserialized or sent over the wire.  It’s simply mapped from the POSTed fields by a ModelBinder.

What if I need to bind custom types to my Model class?

In this case, you’ll want to write your own ModelBinder and register it so that ASP.NET MVC knows to use it when faced with a given type in your application.  Writing a ModelBinder simply requires that you inherit from DefaultModelBinder and override the ConvertType() method.  In order to register your type with its custom ModelBinder, you would do this:

// In Global.asax Application Start or an HttpModule
ModelBinders.Binders.Add(typeof(Customer), new CustomerBinder());

Here is a good example of writing a custom ModelBinder class.

 

Doesn’t having the Model referenced in the View eliminate the need for the Controller?

In response to seeing code in the View like this:

<%= ViewData.Model.FirstName %>

Someone asked if this was stepping on the responsibilities of the Controller, since the View was talking directly to the Model.  Here there can be differing opinions.  It’s not strictly required that you pass your Model classes to the View.  You can simply pass in collections of strings via ViewData.  Another common approach is to use a DTO (Data Transfer Object) or ViewModel (the “VM” in the MVVM palindrome pattern) which typically is a subset of one or more Model classes in your application that contains only the information necessary for a given View.  It’s also not uncommon to pass your Model into the View.  It’s a matter of preference and there are pros and cons to these approaches.

What is not recommended is for your Model (the Customer in my example here, which has a FirstName property) to be persistence aware and to magically go and fetch itself from the database when it is referenced by the View.  The above code should only work if the Controller has explicitly passed in the Model to the View, using code like this:

public void View(int id)
{
  Customer customer = customerRepository.GetCustomer(id);
  return View(customer);
}
It is still the responsibility of the Controller to pass in whatever data the view needs.  The view should not have this responsibility.  Nor should the model know how to persist itself.  This separation of concerns ensures loose coupling, testability, and a more flexible design.
 
An example of using a ViewModel/DTO would be something like this:
public class CustomerSummaryViewModel
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public DateTime DateOfLastPurchase { get; set; }
  publi DateTiem AmountOfLastPurchase { get; set; }
}
...
public void Summary(int id)
{
  Customer customer = customerRepository.GetCustomer(id);
  Order order = orderRepository.List(id).Take(1); // gets most recent order
  var summary = new CustomerSummaryViewModel() {
    FirstName = customer.FirstName,
    LastName = customer.LastName,
    DateOfLastPurchase = order.DatePurchased,
    AmountOfLastPurchase = order.Amount };
 
  return View(summary);
}

Using this approach, the View will have access only to the exact pieces of data that it requires, and no more.  If you want to ensure that the View never calls methods on your Model objects or you want to verify with tests that your View is receiving exactly the data that it needs, then using this ViewModel/DTO approach can be valuable.

    kick it on DotNetKicks.com

Wednesday, 10 June 2009

Comments

 avatar

Clay said on 10 Jun 2009 at 4:08 PM

Thank you for the talk last night! I enjoyed it. I wanted to point out that in binding, blind reliance on automatic model binding can be a security threat.

This link titled "ASP.NET MVC - Think Before You Bind" explains some possible issues...

css.dzone.com/.../aspnet-mvc-thin


 avatar

Haacked said on 10 Jun 2009 at 11:15 PM

Regarding the question of "Doesn’t having the Model referenced in the View eliminate the need for the Controller?" I always point out that this doesn't mean the Controller is not needed because it's the controller that's responsible for handling the user input and handing the model to the view in the first place.

The view referencing the model is perfectly valid. See the diagram of the MVC pattern here: en.wikipedia.org/.../File:ModelViewC

Typically, people forget that the "Model" is an application or presentation model, and not necessary a domain model (though for simplicity demos, I sometimes pass in a domain model).


 avatar

Arnis L. said on 11 Jun 2009 at 11:26 AM

Are there any best practices for populating entities for controller action by id found in form nameValueCollection? So far it seems for me that there aren't much info about binding when it gets more complicated.


ssmith avatar

ssmith said on 11 Jun 2009 at 12:33 PM

@Haacked,

Agree 100%. I covered in the talk the concept of using a "viewmodel" or "DTO" or presentation "model" - it's annoying that there isn't a single definitive term for "the model the UI needs to work with" as opposed to the domain model.