Daily Archives: December 24, 2009

An intuitive Dictionary Model Binder for ASP.NET MVC

The other day I was working on an ASP.NET MVC website and came across a need to post an array from the browser into the web app. The framework comes with something called a Model Binder that automagically converts submitted form data into action parameters of the controller. For example, if we have form submitted data such as
person.FirstName=John&person.LastName=smith
for a theoretical model class ‘Person’, and
[csharp]public ActionResult SavePerson(Person person)[/csharp]
as the action method signature, SavePerson will be executed with the parameter equivalent to
[csharp]new Person() { FirstName = "John", LastName = "Smith" }[/csharp]

The default model binder is pretty powerful, using reflection to dig out and assign all the fields. It also supports arrays and dictionaries, but with big limitations. The array must start at 0 and be unbroken. That is understandable for arrays, but what if you had a dictionary? Surely it can start at any position? Not so. The dictionary has even more obscure requirements, with the need to specify explicit .Key and .Value parameters in your form submission. For example:
dict[0].Key=mykey&dict[0].Value=myvalue
This represented extra work to generate the form on the client side. I just want to input something more intuitive like:
dict[mykey]=myvalue
The ASP.NET MVC framework is highly extensible. It allows you to define your own custom model binder so that’s exactly what I did. Inheriting off DefaultModelBinder, I created DefaultDictionaryBinder that overrode the BindModel method and intercepts when a IDictionary<,> class is being bound.

The code is now up at github: DefaultDictionaryBinder.cs. Note that if you are using this on ASP.NET MVC 1, please define the macro ASPNETMVC1. If you are using it with MVC 2 or MVC 3, it should work as is.

To use, you have to override the default model binder. In global.asax.cs in Application_Start(), add the line:
[csharp]
ModelBinders.Binders.DefaultBinder = new DefaultDictionaryBinder();
[/csharp]

The code is very flexible, only requiring the dictionary key to be of a basic type convertible from string, ie. Dictionary or Dictionary. The value can be any object that is able to be bound by the default model binder.
An example follows:
If your form input is
persons[3].FirstName=John&persons[3].LastName=Smith&persons[4].FirstName=Jane&persons[4].LastName=Doe&
and our action signature
[csharp]public ActionResult SavePersons(Dictionary<int, Person> persons)[/csharp]
the persons parameter would be
[csharp]
new Dictionary<int, Person>() {
{ 3, new Person() {"John", "Smith"} },
{ 4, new Person() {"Jane", "Doe"} },
}
[/csharp]

Simple and intuitive.

Download: DefaultDictionaryBinder.cs Simple Example Project (ASP.NET MVC 3 required)