I ran into a “weirdness” the other day while working up some sample code for this little book I’m writing and thought I would share it with the community since hit me sideways. I was using ASP.NET MVC Beta.
When Attributes Are IQueryable
There is currently an overload for Html.DropDownList that accepts an Anonymous object, which gets “serialized” into html attributes (many of the Html Helpers have this option). So you can write something like this:
<%=Html.DropDownList("CategoryID",new {_class="input})%>
and the HTML created will look something like this:
<select name="CategoryID" id="CategoryID" class="input">...
It has come up many times on the ASP.NET MVC forums that having an untyped object that is serialized this way is causing issues with overloads because “object” is everything, and the method overload with “object” will always win over its typed brethren.
This happened to me yesterday, when creating some sample data, and the results were not-so-good.
The Setup
I wanted to create an HTML DropDown which used jQuery to filter a table when the value of the DropDownList changed. I was using Northwind, and the DropDownList contained Category data which was supposed to filter a table of Products.
I setup the Controller to creaet a SelectList, like so:
NorthwindDataContext db = new NorthwindDataContext();var categories = from c in db.Categories select c;ViewData["CategoryID"] = new SelectList(categories, "CategoryID", "CategoryName");
Then, in my View I rendered the DropDownList:
<%=Html.DropDownList("CategoryID",ViewData["CategoryID"])%>
And all appeared to work well:
The Problem
The Ajax worked, jQuery worked perfectly, the sun came out and the birds were chirping. The problem came in when I went to copy/paste the HTML source – this is what I saw:
<select DataTextField="CategoryName" DataValueField="CategoryID" Items="SELECT [t0].[CategoryID], [t0].[CategoryName], [t0].[Description], [t0].[Picture]FROM [dbo].[Categories] AS [t0]" SelectedValue="" SelectedValues="" id="CategoryID" name="CategoryID"><option value="1">Beverages</option><option value="2">Condiments</option><option value="3">Confections</option><option value="4">Dairy Products</option><option value="5">Grains/Cereals</option><option value="6">Meat/Poultry</option><option value="7">Produce</option><option value="8">Seafood</option></select>
As you can imagine, I was a tad surprised to see SQL in my Select tag
.
I was at a loss for a bit, wondering what I possibly could have done to make this happen. SQL? What would cause a SQL statement to be output? After some emailing, Brad Wilson hit on it (and I let out the obligatory “oh… duh…”):
You know what? I bet you end up calling the “object htmlAttributes” version of Html.DropDownList, because of course ViewData["name"] returns object.
The problem here was my own, of course and it started by this line of code, here:
var categories = from c in db.Categories select c;The “var” here is an IQueryable that is resolved to a SQL statement if you ask it what it is (ToString() is called – and then creates a SQL statement) – so “Items” of the SelectList was serialized “ToString()” and boom – SQL. If I had used “db.Categories” instead, there would be no SQL – same with a List<Category>. I just happened to pick the worst possible data type for SelectList
.
The next weirdness in the chain of weirdnesses was that I complied with a very cool convention of ASP.NET MVC – if you name your SelectList in ViewData the same as your Html.DropDownList, the HtmlHelper will bind your options for you magically. I complied with this – so there was no need for me to specify my SelectList at all (another “duh” moment for me).
The Solution
All I had to do was this:
<%=Html.DropDownList("CategoryID"])%>
And everything would have rendered perfectly.
Analysis
My main problem was getting confused (in my haste to get the sample bits ready) by this overload:
I don’t know why I thought I had to specify it, and moreover I don’t know why it didn’t occur to me that ViewData[ANYTHING] will return an object (and thus call the wrong overload). But it didn’t – and I made what could have been an embarrassing and costly mistake
– more in terms of feeling silly during a code review (as my code reviews tend to involve all of you guys
rather than a security problem.
The good news is that I let Phil and the team know and they’re working (as always) on refining the HTML Helpers to address these (and other) issues.
The main thing for me is that I wouldn’t have caught this if I didn’t view the source (which I usually do no matter what… but you never know…). Everything worked as it should since all the options were there, and the tag had an ID and Name setting, so when posted to the Controller all the values were being pushed along as you would expect.
Hope this helps
. Always view your HTML source – no matter how it’s created
.
