Wednesday, July 14, 2010

Model binding fails for some controller actions

One of the advantages of using the ASP.NET MVC framework is the model binding provided by the framework. Today I found the solution to a problem that has been plaguing me for over a month. Here's a summary:

Let's say you have a form that you want to use to create (and edit) objects of class Badger. So you build an HTML form using the strongly typed HTML helpers available in MVC2 and have your view inherit from System.Web.Mvc.ViewPage .

The action that deals with the form post has the following signature:

[
HttpPost]
public
ActionResult Create(Badger badger) { ... }

When you inspect the HTML generated for the form used to create a new instance of a Badger, you'll see form elements with names such as "Id", "Name", etc.

All is working well so far, the form posts to the Create action in the BadgerController and your badger is created.

Now let's change the view to take in a ViewModel that contains a Badger instance as well as some other useful objects (e.g. a SelectList to choose fur colour), i.e. the view now inherits from System.Web.Mvc.ViewPage

You adjust your strongly typed HTML helpers accordingly and note that the form elements in the generated HTML now have names like "Badger.Id", "Badger.Name", etc.

Still all works well - model binding examines the prefixes used on the form elements and deduces that, even though the form was constructed using a BadgerViewModel, it's actually a Badger object that the form is referring to.

All still works well so far.

The system breaks down if you get a bit lazy with your typing and change your action signature to

[HttpPost]
public
ActionResult Create(Badger b) { ... }


In this case you'll receive a badger object in the action with all properties set to null (or defaults if they are value types like Integer). These nulls will cause all sorts of exceptions in your application which mask the real problem.

The solution is that the instance name of the Badger object must match exactly the prefixes that are used in the HTML form that gets posted. Typically, if you're using the strongly typed HTML helpers, these prefixes will simply be the class name (Badger) which means that the instance name in the action parameter must also be "badger".