Binding in ASP.NET MVC

Posted by: Steven Smith, on 10 Jun 2009 | View original | Bookmarked: 0 time(s)

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?
  • Doesnt 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 thats 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 dont like the magic string used in this, you might look at Eric Hexters 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
}
[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.  Its simply mapped from the POSTed fields by a ModelBinder.

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

In this case, youll 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.

 

Doesnt 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.  Its 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.  Its also not uncommon to pass your Model into the View.  Its 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);
}
separation of concerns ensures loose coupling, testability, and a more flexible design.
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.

Advertisement
Free Agile Project Management Tool from Telerik
TeamPulse Community Edition helps your team effectively capture requirements, manage project plans, assign and track work, and most importantly, be continually connected with each other.
Category: ASP.NET | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 2091 | Hits: 63

Similar Posts

  • Client side Model binding with ASP.NET MVC 3 and KnockoutJs more
  • Building an Senchas ExtJS 4.0 MVC Application With Microsofts ASP.NET MVC3 Series / Basics more
  • Asp.Net MVC Model Binding -Part2 more
  • MvcContrib grid paging and searching in Asp.NET MVC3 more
  • Use MvcContrib Grid to Display a Grid of Data in ASP.NET MVC more
  • Asp.Net MVC Model Binding -Part1 more
  • Improving ASP.NET MVC Application Performance at MVCConf more
  • Top 10 reasons to get excited about mvcConf (the Virtual ASP.NET MVC Conference) on February 8, 2011 more
  • ASP.NET MVC Grid View - Data Binding To Large Database Using LINQ more
  • Just Launched mvc.devexpress.com - New ASP.NET MVC Extensions Demo Website more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD