DropDownLists and SelectItems - Adventures in Modelbinding

Posted by Tom on 2014-03-01 19:21

Every time I think I've got ASP.NET MVC's modelbinding worked out it finds new ways of proving me wrong.

Typically we don't use dropdown lists, as we go for client-side auto-completes and constrained textboxes. But this time someone else has used one and I was under too tight of a time budget to rip it out and bring it in line with the rest of the system. Stop looking at me like that. Stop it.

And try as I might I could not get the bastard to databind to my model.

Let's start with a class. The simplest class we can.

namespace MvcApplication1.Models
{
    public class Simple
    {
        public int Id;
    }
}

And then a simple controller.

public class TestController : Controller
{
    public ActionResult Index()
    {
        var items = new List<object> { 
            new { Value = 1, Text = "One" }, 
            new { Value = 2, Text = "Two" }, 
            new { Value = 3, Text = "Three" }
        };

        ViewBag.SelectList = new SelectList(items, "Value", "Text");
        ViewBag.ItemList = items;

        return View(new Simple() { Id = 2 });
    }
}

The two objects we're putting in the viewbag are a SelectList, already populated with our items, and the raw items themselves. There's no reason to use both outside of this sample code. They're just there so we can look at the different ways of constructing our dropdown.

To the front end!

<div>
<%= Model.Id %>
</div>

<div>
<%= Html.DropDownListFor(m => m.Id, ViewBag.SelectList as SelectList) %>
</div>

<div>
<%= Html.DropDownListFor(m => m.Id, new SelectList(ViewBag.ItemList, "Value", "Text")) %>
</div>

<div>
<%= Html.DropDownListFor(m => m.Id, new SelectList(ViewBag.ItemList, "Value", "Text", Model.Id)) %>
</div>

The first and second should be functionally equivalent (it's just a matter of where we create the SelectList), but I've stopped taking anything for granted when it comes to modelbinding, so I thought I'd check anyway. Regardless, they don't work. Only the last one binds correctly. And that's because we're essentially doing it ourselves - that last argument is us saying we want to use the Id in our model as the selected value. My issue is that it flies in the face of how every other Html.InputTypeFor() helper works.

I guess you could argue that we notice these things because they work so well the rest of the time. Honestly, when was the last time you had to manually set the value of an input? But every time I wrestle with the idiosynchracies of the .NET modelbinder I find myself getting pushing closer to binding everything client-side and just pushing up to something RESTy. Business rules and data access. Get out of my UI, dot NET. You're in the way.

Still, at least it's not as pants-on-head crazy as their radio button handling . . .