MVCContrib FluentHTML Select Lists and NHibernate
When using ASP.NET MVC you will eventually want to do a select (drop down) or even a multiple select list, and your first though might be to use <%= Html.DropDownList %>. Unfortunately you will soon notice that ASP.NET MVC always looks for a match between the name of the dropdown and a property on the model, and if it finds a match, it OVERRIDES the selected value(s) of the select list. Now of course not being able to reliably set the selected value(s) is a major problem – if you Google this you will get a ton of results and most people solve the issue by just changing the name of the Html.DropDownList(“name”) to something that doesn’t match a model property.
Of course we can put this into the “hack” or “workaround” category, and I don’t like doing that on my nice shiny MVC project. However, the biggest problem is if you are doing advanced model binding (I have my own impl, but as an example look at SharpArchitecture), changing the name of the drop down will break model binding and model state errors.
So – how do we use drop down lists and maintain our control over naming and selected items? Enter MVCContrib’s FluentHTML:
MVCContrib FluentHTML Select and MultiSelect
Once you copy the MVCContrib.FluentHTML DLL into your project and resolve the namespace, you get a whole bunch of nice Html helpers right on the base View Data Container. We want to look at the Select and MultiSelect options, which you would use as follows:
<%= this.MultiSelect("User.Projects").Options(Model.Projects) %>
Now moving past the default case, you’ll want to choose which properties to bind and maybe which values are selected. The following is how I attempted to do this at first:
<%= this.MultiSelect("User.Projects")
.Options(new MultiSelectList(Model.Projects, "ID", "Name",
Model.User.Projects.Select(a=>a.ID)))%>
<%= this.MultiSelect("User.Projects")
.Options(new MultiSelectList(Model.Projects, "ID", "Name"))
.Selected(Model.User.Projects.Select(a=>a.ID)) %>
The Problem
When using NHibernate, or likely any other ORM, you run into a subtle problem. Occasionally you will get the following errors when binding MultiSelect (note this problem also happens using the Select):
Object does not match target type.
Exception Details: System.Reflection.TargetException: Object does not match target type.
The Solution
The solution here is to use an overload of Options() to put the valueField and textField into the method call and NOT into the constructor of a SelectList/MultiSelectList. You can either use strings of selectors, as you can see below:
<%= this.MultiSelect("User.Projects")
.Options(Model.Projects, "ID", "Name")
.Selected(Model.User.Projects.Select(a=>a.ID)) %>
<%= this.MultiSelect("User.Projects")
.Options(Model.Projects, a=>a.ID, a=>a.Name)
.Selected(Model.User.Projects.Select(a=>a.ID)) %>