views:

136

answers:

0

I have what I would think is a somewhat normal situation where I need to bind form posts to an "order" model. This model has a few levels of information to it:

Order.Billing.FirstName
Order.Billing.Address.City
Order.Billing.Address.Country

Using the DefaultModelBinder, if I POST a form to an action that takes this Order model as the param, the following fields JustWork(TM):

<%=Html.TextBox("Billing.FirstName")%>
<%=Html.TextBox("Billing.Address.City")%>

This field does not:

<%=Html.TextBox("Billing.Address.Country")%>

The wrinkle I have is with the country property. In our case, Address.Country returns a Country class instance (ISO2/3/Name/Code logic). It is not a string. Not surprise that it doesn't work by default.

My first thought was to create a CountryModelBinder (inherit DefaultModelBinder) and ModelBinders.Binders.Add it to the type of Country. When I do that, CountryModelBinder never gets called in the scenerio above.

My second thought was to create an AddressModelBinder (inherit DefaultModelBinder) and bind it to our Address type. While that does get called, the SetProperty call for "Country" has an empty value, even though the form has posted a field called "Billing.Address.Country".

After some tinkering, it appears that the model binding behavior only calls CreateModel when the model is the top level class the action wants, and all other binders have their BindPropery/SetProperty called for child properties.

In other words, if I create model binders for Order, OrderAddress(Billing), Address, and Country. For the action that takes an order, only OrderModelBinder.CreateModel is called. ORderAddress and Address.BindProperty/SetProperty are called for some things, and sometimes SetProperty value argument is empty when it was clearly posted in a name that matches the other field property mappings.

It's easy enough to just add code to OrderModelBinder to pull Billing.Address.Country out of Request.Form. But I have multiple models that use Address and having all of them do that seems broken.

What am I missing here? Is there a way to have the CountryModelBinder actually get called in this case? I would think that the CountryModelBinder should get called when Billing.Address.Country is mapped to the Country property of the Address binder.