asp.net mvc comments edit

Pop quiz. What would you expect these three bits of HTML to render?

<!-- no new lines after textarea -->
<textarea>Foo</textarea>

<!-- one new line after textarea -->
<textarea>
Foo</textarea>

<!-- two new lines after textarea -->
<textarea>

Foo</textarea>

The fact that I’m even writing about this might make you suspicious of your initial gut answer. Mine would have been that the first would render a text area with “Foo” on the first line, the second with it on the second line, and the third with it on the third line.

In fact, here’s what it renders using Firefox 3.0.3. I confirmed the same behavior with IE 8 beta 2 and Google Chrome.

three text
areas

Notice that the first two text areas are identical. Why is this important?

Suppose you’re building an ASP.NET MVC website and you call the following bit of code:

<%= Html.TextArea("name", "\r\nFoo") %>

You probably would expect to see the third text area in the screenshot above in which “Foo” is rendered on the second line, not the first.

However, suppose our TextArea helper didn’t factor in the actual behavior that browsers exhibit. You would in this case get the behavior of the second text area in which “Foo” is still on the first line.

Fortunately, our TextArea helper does factor this in and renders the supplied value on the next line after the textarea tag. In my mind, this is very much an edge case, but it was reported as a bug by someone external a while back. Isn’t HTML fun?! ;)

asp.net, asp.net mvc comments edit

One of the relatively obscure features of ASP.NET view rendering is that you can render a single view using multiple view engines.

Brad Wilson actually mentioned this in his monster blog post about Partial Rendering and View Engines in ASP.NET MVC, but the implications may have been lost amongst all that information provided.

One of the best features of this new system is that your partial views can use a different view engine than your views, and it doesn’t require any coding gymnastics to make it happen. It all comes down to how the new view system resolves which view engine renders which views.

Let’s dig into a brief example of this in action to understand the full story. Lately, I’ve been playing around with the Spark view engine lately and really like what I see there. Unlike NHaml which pretty gets rid of all angle brackets via a terse DSL for generating HTML, Spark takes the approach that HTML itself should be the “language” for defining the view.

This is not to say that one is specifically better than the other, I’m just highlighting the difference between the two.

Let’s take a look at a small snippet of Spark markup:

<ul>
  <li each='var p in ViewData.Model.Products'>
    ${p.Name} ${Html.ActionLink("Edit Product", "Edit")}
  </li>  
</ul>

Notice that rather than embedding a for loop in the code, you apply the each attribute to a piece of markup to denote that it should be repeated. This is much more declarative than using code nuggets to define a for loop.

As a demonstration, I thought I would take the default ASP.NET MVC project template and within the Index.aspx view, I would render a partial using the Spark view engine.

After referencing the appropriate assemblies from the Spark project, Spark.dll and Spark.Mvc.dll, I registered the spark view engine in Global.asax.cs like so:

protected void Application_Start() {
    ViewEngines.Engines.Add(new SparkViewFactory());
    RegisterRoutes(RouteTable.Routes);
}

I then created a small spark partial view…

<div class="spark-partial">
    <if condition='ViewData.ContainsKey("Title")'>
        <h1>${ViewData["Title"]}</h1>    
    </if>
    
    <p>${ViewData["Message"]}</p>
    <p>${Html.ActionLink("About Page", "About")}</p>
</div>

… and added it to the appropriate directory. I also added a bit of CSS to my default stylesheet in order to highlight the partial.

views-spark-partial

In my Index.aspx view, I added a call to the Html.RenderPartial helper method.

<p>This is a WebForm View.</p>
<p>But the bordered box below, is a partial rendered using Spark.</p>
<% Html.RenderPartial("SparkPartial"); %>

And the result…

spark partial view
result

When we tell the view to render the partial view named “SparkPartial”, ASP.NET MVC will ask each registered view engine, “Hey, yous happens to knows a partial who goes by the name of SparkPartial? I have some unfinished bidness wid this guy.”

Yes, our view infrastructure speaks like a low level mobster thug.

The first view engine that answers yes, gets to render that particular view or partial view. The benefit of this is that if you create a partial view using one view engine, you can reuse that partial on another site that might use a different view engine as its default.

If you want to try out the demo I created, download it and give it a twirl. It is built against ASP.NET MVC Beta.

asp.net mvc, asp.net comments edit

Quick question? What’s higher than a kite?

No, it’s not me nor Cheech and Chong. It’s a cloud!

Bad jokes (but funny video link) aside, Windows Azure, Microsoft’s foray into cloud computing, is getting a lot of attention right now. The basic idea behind cloud computing is you can host your application in the cloud and pay a monthly fee much like a utility such as paying for water and power.

The benefit is you don’t have to deal with the infrastructure work and maintenance and you get “elastic” scalability, meaning your application can dynamically scale to meet rising need without much work on your part. That’s the idea at least.

The Saturday evening before I left for the PDC, Eilon (lead dev for ASP.NET MVC) and I got together to try out ASP.NET MVC with the Azure Dev Fabric environment. This was something we promised to prototype for Jim Nakashima of the Azure team before the PDC, but were … ah … a little late to deliver. ;)

We had good reason as we had been pre-occupied with getting the Beta release out, but still felt bad for totally dropping the ball, hence the late Saturday night pair programming session.

It turned out that it didn’t take long to get the default ASP.NET MVC project template sample app running in the Dev Fabric, which Jim later posted to his blog. Unfortunately, we didn’t invite a QA person over that evening and didn’t test the entire site thoroughly. While the home and about page worked, the account pages requiring the Membership Provider didn’t. Doh!

Fortunately Jim recently updated the sample to now work with ASP.NET Providers running in the cloud and posted it to his blog. Even before Jim updated the sample we delivered to him, Aaron Lerch posted his own step by step guide to getting providers to work. Nice!

The sample project Jim posted has some fixes to the project that allow it to work in the actual cloud. There were a couple of minor bugs regarding rendering URLs with port numbers when using our form helpers (already fixed in our trunk) that would not affect most people, but does happen to affect running in the cloud.

 

asp.net, asp.net mvc comments edit

I’ve heard a lot of concerns from people worried that the ASP.NET team will stop sparing resources and support for Web Forms in favor of ASP.NET MVC in the future. I thought I would try to address that concern in this post based on my own observations.

At the PDC, a few people explicitly told me, not without a slight tinge of anger, that they don’t get ASP.NET MVC and they like Web Forms just fine thank you very much. Hey man, that’s totally cool with me! Please don’t make me the poster boy for killing Web Forms. If you like Web Forms, it’s here to stay. ;)

I can keep telling you that we’re continuing to invest in Web Forms until my face is blue, but that probably won’t be convincing. So I will instead present the facts and let you draw your own conclusions.

ASP.NET Themes

If you watch the ASP.NET 4.0 Roadmap talk at PDC, you’ll see that there are five main areas of investment that the ASP.NET team is working on. I’ll provide a non-comprehensive brief summary of the five here.

Core Infrastructure

With our core infrastructure, we’re looking to address key customer pain points and improve scale and performance.

One feature towards this goal is cache extensibility which will allow plugging in other cache products such as Velocity as a cache provider. We’ll also enhance ASP.NET Session State APIs. There are other scalability investments I don’t even personally understand all too deeply. ;)

To learn more about our cache extensibility plans, check out this PDC talk by Stefan Schackow.

Web Forms

In WebForms, we’re looking to address Client IDs which allow developers to control the id attribute value rendered by server controls. We’re adding support for URL routing with Web Forms. We’re planning to improve ViewState management by providing fine grain control over it. And we’re making investments in making our controls more CSS friendly. There are many other miscellaneous improvements to various control we’re making that would require me to query and filter the bug database to list, and I’m too lazy to do that right now.

AJAX

With Ajax, we’re implementing client side templates and data binding. Our team now owns the Ajax Control Toolkit so we’re looking at opportunities to possibly roll some of those server controls into the core framework. And of course, we’ve added jQuery to our offerings along with jQuery Intellisense.

To see more about our investments here, check out Bertrand Le Roy’s Ajax talk at PDC.

Data and Dynamic Data

In Dynamic Data (which technically could fall in the Web Forms bucket) we’re looking to add support for an abstract data layer which would allow for POCO scaffolding. We’re implementing many-to-many relationships, enhanced filtering, enhanced meta-data, and adding new field templates.

There’s a lot of cool stuff happening here. To get more details on this, check out Scott Hunter’s Dynamic Data talk at PDC.

ASP.NET MVC

We’re still working on releasing 1.0. In the future, we hope to leverage some of the Dynamic Data work into ASP.NET MVC.

Notice here that ASP.NET MVC is just one of these five areas we’re investing in moving forward. It’s not somehow starving our efforts in other areas.

Feature Sharing

One other theme I’d like to highlight is that when we evaluate new features, we try and take a hard look at how it fits into the entire ASP.NET architecture as a whole, looking both at ASP.NET MVC and ASP.NET Web Forms. In many cases, we can share the bulk of the feature with both platforms with a tiny bit of extra work.

For example, ASP.NET Routing was initially an ASP.NET MVC feature only, but we saw it could be more broadly useful and it was shared with Dynamic Data. It will eventually make its way into Web Forms as well. Likewise, Dynamic Data started off as a Web Forms specific feature, but much of it will make its way into ASP.NET MVC in the future.

Conclusions

It’s clear that ASP.NET MVC is getting a lot of attention in part because it is shiny and new, and you know how us geeks loves us some shiny new toys. Obviously, I don’t believe this is the only reason it’s getting attention, as there is a lot of goodness in the framework, but I can see how all this attention tends to skew perceptions slightly. To put it in perspective, let’s look at the reality.

Currently, ASP.NET MVC hasn’t even been released yet. While the number of users interested in and building on ASP.NET MVC is growing, it is clearly a small number compared to the number of Web Form developers we have.

Meanwhile, we have millions of ASP.NET developers productively using Web Forms, many of whom are just fine with the Web Form model as it meets their needs. As much as I love the ASP.NET MVC way of doing things, I understand it doesn’t suit everyone, nor every scenario.

So with all this investment going on, I hope it’s clear that we are continuing to invest in Web Forms along with the entire ASP.NET framework.

code comments edit

UPDATE: I added three new unit tests and one interesting case in which the three browser render something differently.

Well I’m back at it, but this time I want to strip all HTML from a string. Specifically:

  • Remove all HTML opening and self-closing tags: Thus <foo> and <foo /> should be stripped.
  • Remove all HTML closing tags such as </p>.
  • Remove all HTML comments.
  • Do not strip any text in between tags that would be rendered by the browser.

This may not sound all that difficult, but I have a feeling that many existing implementations out there would not pass the set of unit tests I wrote to verify this behavior.

I’ll present some pathological cases to demonstrate some of the odd edge cases.

We’ll start with a relative easy one.

<foo title=">" />

Notice that this tag contains the closing angle bracket within an attribute value. That closing bracket should not close the tag. Only the one outside the attribute value should close it. Thus, the method should return an empty string in this case.

Here’s another case:

<foo =test>title />

This is a non quoted attribute value, but in this case, the inner angle bracket should close the tag, leaving you with “title />”.

Here’s a case that surprised me.

<foo<>Test

That one strips everything except “<>Test”.

It gets even better…

<foo<!>Test

strips out everything except “Test”.

And finally, here’s one that’s a real doozy.

<foo<!--foo>Test

Check out how FireFox, IE, and Google Chrome render this same piece of markup.

weird-markup-chrome

weird-markup-ff

weird-markup-ie

One of these kids is doing his own thing! ;) For my unit test, I decided to go with majority rules here (I did not test with Opera) and went with the behavior of the two rather than Firefox.

The Challenge

The following is the shell of a method for stripping HTML from a string based on the requirements listed above.

public static class Html {
  public static string StripHtml(string html) {
    throw new NotImplementedException("implement this");
  }
}

Your challenge, should you choose to accept it, is to implement this method such that the following unit tests pass. I apologize for the small font, but I used some long names and wanted it to fit.

[TestMethod]
public void NullHtml_ThrowsArgumentException() {
    try {
        Html.StripHtml(null);
        Assert.Fail();
    }
    catch (ArgumentNullException) {
    }
}

[TestMethod]
public void Html_WithEmptyString_ReturnsEmpty() {
    Assert.AreEqual(string.Empty, Html.StripHtml(string.Empty));
}

[TestMethod]
public void Html_WithNoTags_ReturnsTextOnly() {
    string html = "This has no tags!";
    Assert.AreEqual(html, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithOnlyATag_ReturnsEmptyString() {
    string html = "<foo>";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithOnlyConsecutiveTags_ReturnsEmptyString() {
    string html = "<foo><bar><baz />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextBeforeTag_ReturnsText() {
    string html = "Hello<foo>";
    Assert.AreEqual("Hello", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextAfterTag_ReturnsText() {
    string html = "<foo>World";
    Assert.AreEqual("World", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextBetweenTags_ReturnsText() {
    string html = "<p><foo>World</foo></p>";
    Assert.AreEqual("World", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithClosingTagInAttrValue_StripsEntireTag() {
    string html = "<foo title=\"/>\" />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTagClosedByStartTag_StripsFirstTag() {
    string html = "<foo <>Test";
    Assert.AreEqual("<>Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithSingleQuotedAttrContainingDoubleQuotesAndEndTagChar_StripsEntireTag() { 
    string html = @"<foo ='test""/>title' />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithDoubleQuotedAttributeContainingSingleQuotesAndEndTagChar_StripsEntireTag() {
    string html = @"<foo =""test'/>title"" />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttribute_StripsEntireTagWithoutStrippingText() {
    string html = @"<foo title=test>title />";
    Assert.AreEqual("title />", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttributeContainingDoubleQuotes_StripsEntireTagWithoutStrippingText() {
    string html = @"<p title = test-test""-test>title />Test</p>";
    Assert.AreEqual("title />Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttributeContainingQuotedSection_StripsEntireTagWithoutStrippingText() {
    string html = @"<p title = test-test""- >""test> ""title />Test</p>";
    Assert.AreEqual(@"""test> ""title />Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTagClosingCharInAttributeValueWithNoNameFollowedByText_ReturnsText() {
    string html = @"<foo = "" />title"" />Test";
    Assert.AreEqual("Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextThatLooksLikeTag_ReturnsText() {
    string html = @"<çoo = "" />title"" />Test";
    Assert.AreEqual(html, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithCommentOnly_ReturnsEmptyString() {
    string s = "<!-- this go bye bye>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithNonDashDashComment_ReturnsEmptyString() {
    string s = "<! this go bye bye>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTwoConsecutiveComments_ReturnsEmptyString() {
    string s = "<!-- this go bye bye><!-- another comment>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTextBeforeComment_ReturnsText() {
    string s = "Hello<!-- this go bye bye -->";
    Assert.AreEqual("Hello", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTextAfterComment_ReturnsText() {
    string s = "<!-- this go bye bye -->World";
    Assert.AreEqual("World", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithAngleBracketsButNotHtml_ReturnsText() {
    string s = "<$)*(@&$(@*>";
    Assert.AreEqual(s, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithCommentInterleavedWithText_ReturnsText() {
    string s = "Hello <!-- this go bye bye --> World <!--> This is fun";
    Assert.AreEqual("Hello  World  This is fun", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithCommentBetweenNonTagButLooksLikeTag_DoesStripComment() {
    string s = @"<ç123 title=""<!bc def>"">";
    Assert.AreEqual(@"<ç123 title="""">", Html.StripHtml(s));
}


[Test]
public void Html_WithTagClosedByStartComment_StripsFirstTag()
{
    //Note in Firefox, this renders: <!--foo>Test
    string html = "<foo<!--foo>Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

[Test]
public void Html_WithTagClosedByProperComment_StripsFirstTag()
{
    string html = "<FOO<!-- FOO -->Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

[Test]
public void Html_WithTagClosedByEmptyComment_StripsFirstTag()
{
    string html = "<foo<!>Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

What’s the moral of this story, apart from “Phil has way too much time on his hands?” In part, it’s that parsing HTML is fraught with peril. I wouldn’t be surprised if there are some cases here that I’m missing. If so, let me know. I used FireFox’s DOM Explorer to help verify the behavior I was seeing.

I think this is also another example of the challenges of software development in general along with the 80-20 rule. It’s really easy to write code that handles 80% of the cases. Most of the time, that’s good enough. But when it comes to security code, even 99% is not good enough, as hackers will find that 1% and exploit it.

In any case, I think I’m really done with this topic for now. I hope it was worthwhile. And as I said, I’ll post my code solution to this later. Let me know if you find missing test cases.

Technorati Tags: html,parsing

code, regex comments edit

A while ago I wrote a blog post about how painful it is to properly parse an email address. This post is kind of like that, except that this time, I take on HTML.

I’ve written about parsing HTML with a regular expression in the past and pointed out that it’s extremely tricky and probably not a good idea to use regular expressions in this case. In this post, I want to strip out HTML comments. Why?

I had some code that uses a regular expression to strip comments from HTML, but found one of those feared “pathological” cases in which it seems to never complete and pegs my CPU at 100% in the meanwhile. I figure I might as well look into trying a character by character approach to stripping HTML.

It sounds easy at first, and my first attempt was roughly 34 lines of procedural style code. But then I started digging into the edge cases. Take a look at this:

<p title="<!-- this is a comment-->">Test 1</p>

Should I strip that comment within the attribute value or not? Technically, this isn’t valid HTML since the first angle bracket within the attribute value should be encoded. However, the three browsers I checked (IE 8, FF3, Google Chrome) all honor this markup and render the following.

funky
comment

Notice that when I put the mouse over “Test 1” and the browser rendered the value of the title attribute as a tooltip. That’s not even the funkiest case. Check this bit out in which my comment is an unquoted attribute value. Ugly!

<p title=<!this-comment>Test 2</p>

Still, the browsers dutifully render it:

funkier-comment 

At this point, It might seem like I’m spending too much time worrying about crazy edge cases, which is probably true. Should I simply strip these comments even if they happen to be within attribute values because they’re technically invalid. However, it worries me a bit to impose a different behavior than the browser does.

Just thinking out loud here, but what if the user can specify a style attribute (bad idea) for an element and they enter:

<!>color: expression(alert('test'))

Which fully rendered yields: <p style="<!>color: expression(alert('test'))">

If we strip out the comment, then suddenly, the style attribute might lend itself to an attribute based XSS attack.

I tried this on the three browsers I mentioned and nothing bad happened, so maybe it’s a non issue. But I figured it would probably make sense to go ahead and strip the HTML comments in the cases that the browser. So I decided to not strip any comments within an HTML tag, which means I have to identify HTML tags. That starts to get a bit ugly as <foo > is assumed to be an HTML tag and not displayed while <çoo /> is just content and displayed.

Before I show the code, I should clarify something. I’ve been a bit imprecise here. Technically, a comment starts with a – character, but I’ve referred to markup such as <!> as being a comment. Technically it’s not, but it behaves like one in the sense that the browser DOM recognizes it as such. With HTML you can have multiple comments between the <! and the > delimiters according to section 3.2.5 of RFC 1866.

3.2.5. Comments

   To include comments in an HTML document, use a comment declaration. A
   comment declaration consists of `<!' followed by zero or more
   comments followed by `>'. Each comment starts with `--' and includes
   all text up to and including the next occurrence of `--'. In a
   comment declaration, white space is allowed after each comment, but
   not before the first comment.  The entire comment declaration is
   ignored.

      NOTE - Some historical HTML implementations incorrectly consider
      any `>' character to be the termination of a comment.

   For example:

    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <HEAD>
    <TITLE>HTML Comment Example</TITLE>
    <!-- Id: html-sgml.sgm,v 1.5 1995/05/26 21:29:50 connolly Exp  -->
    <!-- another -- -- comment -->
    <!>
    </HEAD>
    <BODY>
    <p> <!- not a comment, just regular old data characters ->

The code I wrote today was straight up old school procedural code with no attempt to make it modular, maintainable, object oriented, etc… I posted it to refactormycode.com here with the unit tests I defined.

In the end, I might not use this code as I realized later that what I really should be doing in the particular scenario I have is simply stripping all HTML tags and comments. In any case, I hope to never have to parse HTML again. ;)

humor, personal comments edit

During my talk at the PDC, I heeded Hanselman’s call to action and decided to veer away from the Northwind “Permademo” and build something different.

In the middle of the talk, I unveiled that I was going to build a competitor to StackOverflow which I would call HaackOverflow. This was all news to Jeff as he hadn’t seen the demo until that point.

The demo walked through some basics of building a standards based ASP.NET MVC application, and sprinkled in a bit of AJAX. At the very end, I swapped out the site.css file and added an image and changed the site from the drab default blue template to something that looked similar in spirit to StackOverflow.

haackoverflow-logo-250

If you haven’t seen the talk yet, you can watch it on Channel9. Also, the links to my slides and the source code are also available on that page.

I only mention it now because Jeff recently Twittered a link to the Reddit Remix of the StackOverflow logo, which reminded of my own hack job on their logo.

Technorati Tags: stackoverflow,pdc08

asp.net, code, asp.net mvc comments edit

Usability and Discoverability (also referred to as Learnability) are often confused with one another, but they really are distinct concepts. In Joel Spolsky’s wonderful User Interface Design for Programmers (go read it!), Joel provides an metaphor to highlight the difference.

It takes several weeks to learn how to drive a car. For the first few hours behind the wheel, the average teenager will swerve around like crazy. They will pitch, weave, lurch, and sway. If the car has a stick shift they will stall the engine in the middle of busy intersections in a truly terrifying fashion. \ If you did a usability test of cars, you would be forced to conclude that they are simply unusable.

Scary
Driver

This is a crucial distinction. When you sit somebody down in a typical usability test, you’re really testing how learnable your interface is, not how usable it is. Learnability is important, but it’s not everything. Learnable user interfaces may be extremely cumbersome to experienced users. If you make people walk through a fifteen-step wizard to print, people will be pleased the first time, less pleased the second time, and downright ornery by the fifth time they go through your rigamarole.

Sometimes all you care about is learnability: for example, if you expect to have only occasional users. An information kiosk at a tourist attraction is a good example; almost everybody who uses your interface will use it exactly once, so learnability is much more important than usability.

Rick Osborne in his post, Usability vs Discoverability, also covers this distinction, while Scott Berkun points out in his post on The Myth of Discoverability that you can’t have everything be discoverable.

These are all exmaples of the principle that there is no such thing as a perfect design. Design always consists of trade-offs.

Let’s look at an example using a specific feature of ASP.NET Routing that illustrates this trade-off. One of the things you can do with routes is specify constraints for the various URL parameters via the Constraints property of the Route class.

The type of this property is RouteValueDictionary which contains string keys mapped to object values. Note that by having the values of this dictionary be of type object, the value type isn’t very descriptive of what the value should be. This hurts learnability, but let’s dig into why we did it this way.

One of the ways you can specify the value of a constraint is via a regular expression string like so:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteValueDictionary {{"foo", "abc.*"}, {"bar", "\w{4}"}};
RouteTable.Routes.Add(route);

This route specifies that the foo segment of the URL must start with “abc” and that the bar segment must be four characters long. Pretty dumb, yeah, but it’s just an example to get the point across.

We figure that in 99.9% of the cases, developers will use regular expression constraints. However, there are several cases we identified in which a regular expression string isn’t really appropriate, such as constraining the HTTP Method. We could have hard coded the special case, which we originally did, but decided to make this extensible because more cases started cropping up that were difficult to handle. This is when we introduced the IRouteConstraint interface.

At this point, we had a decision to make. We could have changed the the type of the Constraints property to something where the values are of type IRouteConstraint rather than object in order to aid discoverability. Doing this would require that we then implement and include a RegexConstraint along with an HttpMethodConstraint.

Thus the above code would look like:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteConstraintDictionary {{"foo", new RegexConstraint("abc.*")}, 
    {"bar", new RegexConstraint("\w{4}")}};
RouteTable.Routes.Add(route);

That’s definitely more discoverable, but at the cost of usability in the general case (note that I didn’t even include other properties of a route you would typically configure). For most users, who stick to simple regular expression constraints, we’ve just made the API more cumbersome to use.

It would’ve been really cool if we could monkey patch an implicit conversion from string to RegexConstraint as that would have made this much more usable. Unfortunately, that’s not an option.

So we made the call to favor usability in this one case at the expense of discoverability, and added the bit of hidden magic that if the value of an item in the constraints dictionary is a string, we treat it as a regular expression. But if the value is an instance of a type that implements IRouteConstraint, we’d call the Match method on it.

It’s not quite as discoverable the first time, but after you do it once, you’ll never forget it and it’s much easier to use every other time you use it.

Making Routing with MVC More Usable

Keep in mind that Routing is a separate feature from ASP.NET MVC. So what I’ve covered applies specifically to Routing.

When we looked at how Routing was used in MVC, we realized we had room for improving the usability. Pretty much every time you define a route, the route handler you’ll use is MvcRouteHandler it was odd to require users to always specify that for every route. Not only that, but once you got used to routing, you’d like a shorthand for defining defaults and constraints without having to go through the full collection initializer syntax for RouteValueDictionary.

This is when we created the set of MapRoute extension methods specific to ASP.NET MVC to provide a façade for defining routes. Note that if you prefer the more explicit approach, we did not remove the RouteCollection’s Add method. We merely layered on the MapRoute extensions to RouteCollection to make defining routes simpler. Again, a trade-off in that the arguments to the MapRoute methods are not as discoverable as using the explicit approach, but they are usable once you understand how they work.

Addressing Criticisms

We spent a lot of time thinking about these design decisions and trade-offs, but it goes without saying that it will invite criticisms. Fortunately, part of my job description is to have a thick skin. ;)

In part, by favoring usability in this case, we’ve added a bit of friction for those who are just starting out with ASP.NET MVC, just like in Joel’s example of the teenager learning to drive. However, after multiple uses, it becomes second nature, which to me signifies that it is usable. Rather than a flaw in our API, I see this more as a deficiency in our documentation and Intellisense support, but we’re working on that. This is an intentional trade-off we made based on feedback from people building multiple applications.

But I understand it won’t please everyone. What would be interesting for me to hear is whether these usability enhancements work. After you struggle to define constraints the first time, was it a breeze the next time and the time after that, especially when compared to the alternative?

asp.net mvc, asp.net, code comments edit

UPDATE: Due to differences in the way that ASP.NET MVC 2 processes request, data within the substitution block can be cached when it shouldn’t be. Substitution caching for ASP.NET MVC is not supported and has been removed from our ASP.NET MVC Futures project.

This technique is NOT RECOMMENDED for ASP.NET MVC 2.

With ASP.NET MVC, you can easily cache the output of an action by using the OutputCacheAttribute like so.

[OutputCache(Duration=60, VaryByParam="None")]
public ActionResult CacheDemo() {
  return View();
}

One of the problems with this approach is that it is an all or nothing approach. What if you want a section of the view to not be cached?

mmmm-doughnut Well ASP.NET does include a <asp:Substitution …/> control which allows you to specify a method in your Page class that gets called every time the page is requested. ScottGu wrote about this way back when in his post on Donut Caching.

However, this doesn’t seem very MVC-ish, as pointed out by Maarten Balliauw in this post in which he implements his own means for adding output cache substitution.

However, it turns out that the Substitution control I mentioned earlier makes use of an existing API that’s already publicly available in ASP.NET. The HttpResponse class has a WriteSubstitution method which accepts an HttpResponseSubstitutionCallback delegate. The method you supply is given an HttpContext instance and allows you to return a string which is displayed in place of the substitution point.

I thought it’d be interesting to create an Html helper which makes use of this API, but supplies an HttpContextBase instead of an HttpContext. Here’s the source code for the helper and delegate.

public delegate string MvcCacheCallback(HttpContextBase context);

public static object Substitute(this HtmlHelper html, MvcCacheCallback cb) {
    html.ViewContext.HttpContext.Response.WriteSubstitution(
        c => HttpUtility.HtmlEncode(
            cb(new HttpContextWrapper(c))
        ));
    return null;
}

The reason this method returns a null object is to make the usage of it seem natural. Let me show you the usage and you’ll see what I mean. Referring back to the controller code at the beginning of this post, imagine you have the following markup in your view.

<!-- this is cached -->
<%= DateTime.Now %>

<!-- and this is not -->
<%= Html.Substitute(c => DateTime.Now.ToString()) %>

On the first request to this action, the entire page is rendered dynamically and you’ll see both dates match. But when you refresh, only the lambda expression is called and is used to replace that portion of the cached view.

I have to thank Dmitry, our PUM and the first developer on ASP.NET way back in the day who pointed out this little known API method to me. :)

We will be looking into hopefully including this in v1 of ASP.NET MVC, but I make no guarantees.

asp.net, asp.net mvc comments edit

UPDATE: I updated the prototype to work against the ASP.NET MVC 1.0 RTM. Keep in mind, this is *NOT* a backport of the the ASP.NET MVC 2 feature so there may be some differences.

A question that this. The funny part with things like this is that I’ve probably spent as much time writing this blog post as I did working on the prototype, if not more!

The scenario that areas address is being able to partition your application into discrete areas of functionality. It helps make managing a large application more manageable and allows for creating distinct applets that you can drop into an application.

For example, suppose I want to drop in a blogs subfolder, complete with its own controllers and views, along with a forums subfolder with its own controllers and views, into a default project. The end result might look like the following screenshot (area folders highlighted).

areas-folder-structure

Notice that these folders have their own Views, Content, and Controllers directories. This is slightly similar to a solution proposed by Steve Sanderson, but he ran into a few problems we’d like to resolve.

  • URL generation doesn’t take namespaces into consideration when generating a URL. We want to be able to easily generate URLs to other areas.
  • When you are within one area, and you call Html.ActionLink to link to another action in the same area, you’d like to not have to specify the area name. You’d also like to not be forced to specify a route name.
  • You still want to be able to link to another area by specifying the area name. And, you want to be able to have controllers of the same name within the same area.
  • You also want to be able to link to the “root” area, aka the default HomeController that comes with the project template that is not located in an area.

The prototype I put together resolves these problems by adopting and enforcing a few constraints when it comes to areas.

  • The area portion comes first in the URL.
  • Controller namespaces must have a specific format that includes the area name in the namespace.
  • The root controllers that are not in any area have a default area name of “root”.
  • When resolving a View/Partial View for a controller within an area, we search in the area’s Views folder first. If not found there, we then look in the root Views folder.

Overridable Templating

This last point bears a bit of elaboration. It is a technique that came about from some experimentation I did on a potential new way of skinning for Subtext.

In the Blogs area, I have a partial view called LoginUserControl.ascx. In the Forums area, I don’t have this partial view. Thus when you go to the Forums area, it falls back to the root Views directory in order to render this partial view. But in the Blogs area, it uses the one specified in the area. This is a convenient way of implementing overridable templating and is reminiscent of ASP.NET Dynamic Data.

If you run the sample, you’ll see what I mean. When you hit the Blogs area, the login link is replaced by some text saying “Blogs don’t need no stinking login”, but the Forums area still has the login link.

Note that all of these conventions are specifically for this prototype. It would be very easy to relax these constraints to fit you’re own way of doing things. I just wanted to show how this could be done using the current ASP.NET MVC bits.

Registering Routes

The first thing we do is call two new extension methods I wrote to register routes for the areas. This call is made in the RegisterRoutes method in Global.asax.cs.

routes.MapAreas("{controller}/{action}/{id}", 
    "AreasDemo", 
    new[]{ "Blogs", "Forums" });

routes.MapRootArea("{controller}/{action}/{id}", 
    "AreasDemo", 
    new { controller = "Home", action = "Index", id = "" });

The first argument to the MapAreas method is the Routing URL pattern you know and love. We will prepend an area to that URL. The second argument is a root namespace. By convention, we will append “.Areas.AreaName.Controllers” to the provided root namespace and use that as the namespace in which we lookup controller types.

For example, suppose you have a root namespace of MyRootNamespace. If you have a HomeController class within the Blogs area, its full type name would need to be

MyRootNamespace.Areas.Blogs.Controllers.HomeController.

Again, this is a convention I made up, it could be easily changed. The nice thing about following this convention is you don’t really have to think about namespaces if you follow the directory structure I outlined. You just focus on your areas.

The last argument to the method is a string array of the “areas” in your application. Perhaps I could derive this automatically by examining the file structure, but I put together this prototype in the morning and didn’t think of that till I was writing this blog post. ;)

The second method, MapRootArea, is exactly the same as MapRoute, except it adds a default of area = “root” to the defaults dictionary.

Registering the ViewEngine

I also wrote a very simple custom view engine that knows how to look in the Areas folder first, before looking in the root Views folder when searching for a view or partial view.

I wrote this in such a way that it replaces the default view engine. To make this switch, I added the following in Global.asax.cs in the Application_Start method.

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaViewEngine());

The code for the AreaViewEngine is fairly simple. It inherits from WebFormViewEngine and looks in the appropriate Areas first for a given view or partial view before looking in the default location. The way I accomplished this was by adding some catch-all location formats such as ~/{0}.aspx and formatted those myself in the code.

If that last sentence meant nothing to you, don’t worry. It’s an implementation detail of the view engine.

Linking to Areas

In the root view, I have the following markup to link to the HomeController and Index action of each area.

<%= Html.ActionLink("Blog Home", "Index", new { area="Blogs" } )%>
<%= Html.ActionLink("Forums Home", "Index", new { area="Forums" } )%>

However, within an area, I don’t have to specify the area when linking to another action within the same area. It chooses the current area by default. For example, here’s the code to render a link to the Blogs area’s Posts action.

<%= Html.ActionLink("Blogs Posts", "Posts") %>

That’s no different than if you weren’t doing areas. Of course, if I want to link to the forums area, I need to specify that. Also, if I want to link to an action in the root, I need to specify that as well.

<%= Html.ActionLink("Forums", "Index", "new {area="forums"}") %>
<%= Html.ActionLink("Root Home", "Index", "new {area="root"}") %>

As you click around in the sample, you’ll notice that I changed the background color when in a different area to highlight that fact.

Next Step, Nested Areas

One thing my prototype doesn’t address are nested areas. This is something I’ll try to tackle next. I’m going to see if I can clean up the implementation later and possibly get them into the MVC Futures project. This is just some early playing around I did on my own so do let me know if you have better ideas for improving this.

Download the Sample

asp.net mvc, asp.net comments edit

With the release of ASP.NET MVC Beta, the assemblies distributed with ASP.NET MVC are automatically installed into the GAC.

  • System.Web.Mvc
  • System.Web.Routing
  • System.Web.Abstractions

While developing an application locally, this isn’t a problem. But when you are ready to deploy your application to a hosting provider, this might well be a problem if the hoster does not have the ASP.NET MVC assemblies installed in the GAC.

Fortunately, ASP.NET MVC is still bin-deployable. If your hosting provider has ASP.NET 3.5 SP1 installed, then you’ll only need to include the MVC DLL. If your hosting provider is still on ASP.NET 3.5, then you’ll need to deploy all three. It turns out that it’s really easy to do so.

Also, ASP.NET MVC runs in Medium Trust, so it should work with most hosting providers’ Medium Trust policies. It’s always possible that a hosting provider customizes their Medium Trust policy to be draconian.

What I like to do is use the Publish feature of Visual Studio to publish to a local directory and then upload the files to my hosting provider. If your hosting provider supports FTP, you can often skip this intermediate step and publish directly to the FTP site.

The first thing I do in preparation is to go to my MVC web application project and expand the References node in the project tree. Select the aforementioned three assemblies and in the Properties dialog, set Copy Local to True.

copy-local-true_3 

Now just right click on your application and select Publish.

 publish-project_3

This brings up the following Publish wizard.

Publish-Web

Notice that in this example, I selected a local directory. When I hit Publish, all the files needed to deploy my app are available in the directory I chose, including the assemblies that were in the GAC.

bin-assemblies

Now I am ready to XCOPY the application to my host, but before I do that, I really should test the application as a bin deployed app to be on the safe side.

Ideally, I would deploy this to some staging server, or a virtual machine that does not have ASP.NET MVC installed. Otherwise, I’m forced to uninstall ASP.NET MVC on the current machine and then test the application.

You might be wondering, as I did, why I can’t just use gacutil to temporarily unregister the assembly, test the app, then use it again to register the assembly. Because it was installed using an MSI, Windows won’t let you unregister it. Here’s a command prompt window that shows what I got when I tried.

gacutil-mvc

Notice that it says that “assembly is required by one or more applications”. In general, there shouldn’t be any difference between running your application with MVC gac’d and it ungac’d. But I wouldn’t trust me saying this, I’d test it out to be sure.

code, personal comments edit

Whew! I’ve finally found a bit of time to write about my impressions of the PDC 2008 conference. If you’re looking for insightful commentary and a “What does this all mean” post, you’ve come to the wrong place. There are plenty of others providing that sort of commentary. I’ll just string together some random impressions and pics from my perspective.

crowd before the MVC
talk First of all, one thing I’m very impressed with is that all sessions are viewable online almost immediately afterwards, with full video of the slides and the presenter. Now I understand why they asked me not to pace too much. To see my presentation, visit its Channel9 Page. Note that my slides and sample code are now up there.

john-lam-and-me I hade a great time delivering this talk (see photo of the room before I started above) and as I wrote before, I uncovered a new presentation tip.

There were 1150 attendees at the talk. If you saw my talk, be sure to submit an eval as the last time I checked, only 95 people did.

mr-open-spacesOf course the best part of the conference (besides excessive amounts of RockBand) is hanging out with the people! I spent time at the ASP.NET lounge at various points in the conference answering a boatload of questions about ASP.NET MVC. This has inspired a nice backlog of posts I should write.

I also spent a lot of time in the Open Spaces area enjoying the geekout sessions. Especially Dustin Campbell’s session on currying, closures, etc… which turned into a discussion of functional languages as I was showing up.

los-tres-amigosOf course, the various parties were great too. I don’t have pictures of the Universal Studios trip, unfortunately, but I will say the Simpson’s ride was awesome. I only brought my camera to the Dev After Dark party, on Wednesday night, which is where most of these are from.

As you can see here, Jeff Atwood, Miguel De Icaza, and myself having a good time at the JLounge. We tried to get it renamed to NLounge, but to no avail.

mike-and-the-dude One of the highlights of the evening was running into my coworker’s hero, The Dude from The Big Lebowski holding a white russian!

Ok, it’s actually Ted Neward graciously posing with my drink as this was probably the 2048^th^ time he heard this joke.

OG-microsoft And wouldn’t you know it, but the Microsofties from the early days showed up to the party. There’s young Bill in the lower left corner.

It was very interesting to meet so many people in person who I’ve “met” before via comments in our forums, on my blog, Twitter, etc…

Especially in the cases where a person in the past reported a bug in a forum, and then at PDC had the opportunity to explain it to me so that we both understood the issue more clearly. In any case, I’m hopefully done travelling for the year.

personal, code comments edit

In my last post, I joked that the reason that someone gave me all 1s in my talk was a misunderstanding of the evaluation form. In truth, I just assumed that someone out there really didn’t like what I was showing and that’s totally fine. It was something I found funny and I didn’t really care too much.

But I received a message from someone that they tried to evaluate the session from the conference hall, but the evaluation form was really screwy on their iPhone. For example, here’s how it’s supposed to look in IE.

survey
ie

I checked it out with Google Chrome which uses WebKit, the same rendering engine that Safari, and thus the iPhone, uses.

Here it is (click to see full size).

survey
webkit

Notice anything different? :)

The good news here is that nothing really at stake here for me, as speaking is a perk of my job, not a requirement. It doesn’t affect my reviews. I’d bet this form has been in use for years and was built long before the iPhone.

However, if we ever start deciding elections online, this highlights one of the subtle design issues the designers of such a ballot would need to address.

It’s not just an issue of testing for the current crop of browsers, it’s also about anticipating what future browsers might do.

Such a form would really need to have simple semantic standards based markup and be rendered in such a way that if CSS were disabled, submitting the form would still reflect the voter’s intention.

For example, it may be hard to anticipate every possible browser rendering of a form. In this case, one fix would be to change the label for the radio buttons to reflect the intention. Thus rather than the number “1” the radio button label would be “1 – Very Dissatisfied”. Sure, it repeats the column information, but no matter where the radio buttons are displayed, it reflects the voter’s intention.

In any case, I think the funny part of this whole thing is when I mentioned this one evaluation score, several people I know all laid claim to being the one who hated my talk. They all want to take credit for hating on my talk, without going through all the trouble of actually submitting bad scores. ;)

If you were at the conference and saw my talk, be sure to evaluate it. And do be kind. :)

UPDATE: Be sure to read John Lam’s account of the PDC as well. He has some great suggestions for conference organizers to improve the evaluation process.

personal comments edit

Before giving a presentation, I review Scott Hanselman’s top 11 presentation tips. Well I have a twelfth tip that Scott needs to add to his list, and he’ll vouch for this.

rockband A couple of hours before Jeff and I gave the ASP.NET MVC presentation (the video is now posted!), we played some RockBand in the Big Room (exhibition area).

Playing Eye of the Tiger before a big talk has a great way of both pumping you up and loosening you up at the same time. When I ran into Scott and told him this tip, he said he did the very same thing, playing Eye of the Tiger on RockBand before his talk. In his case, I think he played for two hours.

In any case, I felt like my talk went well. Jeff was entertaining as always and provided that taste of real-world relevance to what we’re doing with ASP.NET MVC. I particularly liked it when he remoted into his live server and showed the audience how his 8 CPU server was doing via task manager.

In any case, if you watched my talk, be sure to submit evals (higher number means better. Whomever gave me all 1s, that’s just mean! ;). I look forward to hearing your feedback. I’d love to be able to show people that there’s a demand for this type of framework and development model (transparency, source releases, etc…) and maybe we can get more than one MVC talk next time. I think it’s time I do a really advanced one. :)

Technorati Tags: pdc2008,aspnetmvc,presentation,pdc

asp.net, asp.net mvc, code comments edit

Download the sample project to play with the code as you read this blog post.

Using the DefaultModelBinder in ASP.NET MVC, you can bind submitted form values to arguments of an action method. But what if that argument is a collection? Can you bind a posted form to an ICollection<T>?

Sure thing! It’s really easy if you’re posting a bunch of primitive types. For example, suppose you have the following action method.

public ActionResult UpdateInts(ICollection<int> ints) {

  return View(ints);
}

You can bind to that by simply submitting a bunch of form fields each having the same name. For example, here’s an example of a form that would bind to this, assuming you keep each value a proper integer.

<form method="post" action="/Home/UpdateInts">
    <input type="text" name="ints" value="1" />
    <input type="text" name="ints" value="4" />
    <input type="text" name="ints" value="2" />
    <input type="text" name="ints" value="8" />
    <input type="submit" />
</form>

If you were to take fiddler and look at what data actually gets posted when clicking the submit button, you’d see the following.

ints=1&ints=4&ints=2&ints=8

The default model binder sees all these name/value pairs with the same name and converts that to a collection with the key ints, which is then matched up with the ints parameter to your action method. Pretty simple!

Where it gets trickier is when you want to post a list of complex types. Suppose you have the following class and action method.

public class Book {
    public string Title { get; set; }
    public string Author { get; set; }
    public DateTime DatePublished { get; set; }
}

//Action method on HomeController
public ActionResult UpdateProducts(ICollection<Book> books) {
    return View(books);
}

You might think we could simply post the following to that action method:

Title=title+one&Author=author+one&DateTime=1/23/1975 &Title=author+two&Author=author+two&DateTime=6/6/2007…

Notice how we simply repeat each property of the book in the form post data? Unfortunately, that wouldn’t be a very robust approach. One reason is that we can’t distinguish from the fact that there may well be another Title input unrelated to our list of books which could throw off our binding.

Another reason is that the checkbox input does not submit a value if it isn’t checked. Most input fields, when left blank, will submit the field name with a blank value. With a checkbox, neither the name nor value is submitted if it’s unchecked! This again can throw off the ability of the model binder to match up submitted form values to the correct object in the list.

To bind complex objects, we need to provide an index for each item, rather than relying on the order of items. This ensures we can unambiguously match up the submitted properties with the correct object.

Here’s an example of a form that submits three books.

<form method="post" action="/Home/Create">

    <input type="text" name="[0].Title" value="Curious George" />
    <input type="text" name="[0].Author" value="H.A. Rey" />
    <input type="text" name="[0].DatePublished" value="2/23/1973" />
    
    <input type="text" name="[1].Title" value="Code Complete" />
    <input type="text" name="[1].Author" value="Steve McConnell" />
    <input type="text" name="[1].DatePublished" value="6/9/2004" />
    
    <input type="text" name="[2].Title" value="The Two Towers" />
    <input type="text" name="[2].Author" value="JRR Tolkien" />
    <input type="text" name="[2].DatePublished" value="6/1/2005" />
    
    <input type="submit" />
</form>

Note that the index must be an unbroken sequence of integers starting at 0 and increasing by 1 for each element.

The new expression based helpers in ASP.NET MVC 2 will produce the correct format within a for loop. Here’s an example of a view that outputs this format:

<%@ Page Inherits="ViewPage<IList<Book>>" %>

<% for (int i = 0; i < 3; i++) { %>

  <%: Html.TextBoxFor(m => m[i].Title) %>
  <%: Html.TextBoxFor(m => m[i].Author) %>
  <%: Html.TextBoxFor(m => m[i].DatePublished) %> 

<% } %>

It also works with our templated helpers. For example, we can take the part inside the for loop and put it in a Books.ascx editor template.

<%@ Control Inherits="ViewUserControl<Book>" %>

<%: Html.TextBoxFor(m => m.Title) %>
<%: Html.TextBoxFor(m => m.Author) %>
<%: Html.TextBoxFor(m => m.DatePublished) %> 

Just add a folder named EditorTemplates within the Views/Shared folder and add Books.ascx to this folder.

Now change the original view to look like:

<%@ Page Inherits="ViewPage<IList<Book>>" %>

<% for (int i = 0; i < 3; i++) { %>

  <%: Html.EditorFor(m => m[i]) %>

<% } %>

Non-Sequential Indices

Well that’s all great and all, but what happens when you can’t guarantee that the submitted values will maintain a sequential index? For example, suppose you want to allow deleting rows before submitting a list of books via JavaScript.

The good news is that by introducing an extra hidden input, you can allow for arbitrary indices. In the example below, we provide a hidden input with the .Index suffix for each item we need to bind to the list. The name of each of these hidden inputs are the same, so as described earlier, this will give the model binder a nice collection of indices to look for when binding to the list.

<form method="post" action="/Home/Create">

    <input type="hidden" name="products.Index" value="cold" />
    <input type="text" name="products[cold].Name" value="Beer" />
    <input type="text" name="products[cold].Price" value="7.32" />
    
    <input type="hidden" name="products.Index" value="123" />
    <input type="text" name="products[123].Name" value="Chips" />
    <input type="text" name="products[123].Price" value="2.23" />
    
    <input type="hidden" name="products.Index" value="caliente" />
    <input type="text" name="products[caliente].Name" value="Salsa" />
    <input type="text" name="products[caliente].Price" value="1.23" />
    
    <input type="submit" />
</form>

Unfortunately, we don’t have a helper for generating these hidden inputs. However, I’ve hacked together an extension method which can render this out for you.

When you’re creating a form to bind a list, add the following hidden input and it will add the appropriate hidden input to allow for a broken sequence of indices. Use at your own risk!I’ve only tested this in a couple of scenarios. I’ve included a sample project with multiple samples of binding to a list which includes the source code for this helper.

<%: Html.HiddenIndexerInputForModel() %>

This is something we may consider adding to a future version of ASP.NET MVC. In the meanwhile, give it a whirl and let us know how it works out for you.

Technorati Tags: aspnetmvc,modelbinders

personal comments edit

I just can’t help myself. I said I wouldn’t be one of those parents, but forget it. I am one of those parents. I think my kid is adorable, so sue me. Check out Cody’s halloween costume. He’s with his BFF Forever, Alex the Panda.

Meet, Codysaurus!

codysaurus-and-alexpanda

Coming to terrorize a neighborhood near you.

asp.net, asp.net mvc comments edit

Today we finally officially released the beta of ASP.NET MVC (go download it already!).

True, the release has actually been available online since yesterday as it was announced in a Keynote at VSLive by Scott Hanselman, but that was intended to be a special treat for attendees in what ended up being the worst kept secret in .NET-dom.

As usual, to get all the details, check out the latest epic installment on ScottGu’s blog. Scott Hanselman also has a great blog post with good coverage as well.

As I warned before, we no longer bundle the Mvc Futures assembly (Microsoft.Web.Mvc.dll). However, we did just publish a release of this assembly updated for Beta on CodePlex. Source code for the Beta and Futures releases will be pushed to CodePlex shortly. Sorry about the delay but there’s so much work to be done here. :)

One very exciting element to this release is that we’ve included JQuery (as I mentioned before) and are indeed the first Microsoft product to include it. One of the first Microsoft products (AFAIK) to bundle a third-party open source component.

The following list (each a link to the GU’s blog) show’s what’s new in the Beta.

What’s new in ASP.NET MVC Beta?

I hope you enjoy this release and the plan from here on out is primarily to focus on stabilization. This means fixing bugs, making design change requests which we feel hit meet a high bar, and getting the product ready for RTM. However, as Scott mentioned, there are a few new features we’re planning, especially with Visual Studio tooling.

personal comments edit

bowl-mm Today marks my one year anniversary at Microsoft. Tradition dictates that I bring in a pound of M&Ms for each year that I’ve been an employee. I’m going to buck that trend (because I like bucking things) and bring in 1 kilo of Japanese candies. Since I just returned from a trip to Japan and it is also customary to bring gifts back from a trip, this ends up killing two birds with one stone. Software is not the only place to apply the DRY principle.

Looking back at when I first was hired and later at my first days when I drank from the firehose, working here feels a lot different now than it did then. I finally feel a lot more settled at Microsoft, though there are still many days when I feel like a new hire, or even as a bit of an outsider.

The other day in an IM chat with Scott Hanselman, he made some comment about how us Microsofties love our app_codeApp_Data directory. Hey man, his badge is just as blue as mine, but I understand the sentiment. Guys like us are referred to as “industry hires” as opposed to college hires. Many of the developers here happen to be college hires. The theory is that industry hires will bring a fresh perspective, but I think we just mostly get in everyone’s way and stir up trouble.

So what have I done in this past year?

I’ve spoken at five conferences (Tech-Ed Hong Kong, TechReady 6 and 7, DevScovery, MVP Global Summit) and two internal TAP events. I’ve attended two other conferences (Mix 08, Google IO). For the most part, I think I’ve gotten over my stage fright, which was intense in the beginning. Hopefully, I’ve also improved as a speaker, but I still cringe when I hear myself speak.

I’ve been involved in around four or five ASP.NET MVC preview releases (I’ve lost count) as well as the release of ASP.NET 3.5 SP1 (ASP.NET Routing feature) and a CodePlex release of ASP.NET Dynamic Languages support. I’m particularly excited about taking ownership of our DLR support for ASP.NET and hope that in the new year I can push that forward.

I’ve also been involved in division wide efforts to help other teams understand Test Driven Development so that our products moving forward will take TDD into consideration in the design of their products.

Amidst all this, I even found some time to get Subtext 2.0 out the door. Open source software remains a passion for me and I’m very excited about all the progressive changes that have happened here in the past year from us including JQuery in our offering to the opening up of licensing in various products such as MEF.

What’s Next For Me?

I think I’ll stay here for a while. My wife and I really like it in Bellevue and so far, I’m really enjoying the work here. In the next year, I’ll be taking on more responsibilities. I’ll be taking ownership for driving our TDD efforts, hoping to obtain a few small wins here and there. Baby steps.

I’ll also keep driving ASP.NET MVC towards RTM and put together a plan and strategy together for our Dynamic Languages effort.

I’m still trying to wrap my head around the PM role here at Microsoft. My managers never fail to remind me that while I’m doing fine at the technical and community side of things, I really need to improve the project management side of things. Understandable, I’m doing great at the fun stuff, but not so good at the part that I don’t find so fun. :)

In any case, as I did before, I want to thank many of you for helping me with my job. There have been many large design improvements to ASP.NET MVC that quite possibly wouldn’t have happened were it not for your constant feedback. It really is appreciated.

asp.net comments edit

I’ve used the term “drinking from the fire hose” when describing my first days at Microsoft. However, I believe that a lot of our customers feel this way when approaching the plethora of options for web application development on the Microsoft stack.

This is feedback we’ve received from many sources and as Scott Hanselman pointed out, there’s a concerted effort to make things easier to find and understand here. Much of these efforts will take time to see fruition, but some of them are happening now.

The new Microsoft Web Platform Installer Beta can get you up and running developing on ASP.NET in two easy clicks.

First, you select an option from the first screen. Obviously, if you choose the Your Choice option, it’ll take more than two clicks.

choose-type

I chose the ASP.NET Developer option and clicked Next.

accept-license

The second screen lets you choose to accept the licensing terms. This is nice in that you don’t have to go and click through 20 different EULAs.

installing

Once you click I Accept the installer begins.

If you want more control, you can choose Your Choiceat the first screen which takes you to a screen that lets you choose all sorts of options.

your-choice

This is a great tool for someone who wants to quickly get started learning and developing on ASP.NET. You’ll notice that it doesn’t include ASP.NET MVC yet. Don’t worry, it will once we have a proper beta release.

If you run into any problems with it, be sure to report it on their forums. For more information, be sure to read the announcement on Bill Staple’s blog.

In case you missed my first link to the beta for the installer, it’s here.

code comments edit

UPDATE: Pretty much disregard this entire post, except as a reminder that it’s easy to make a mistake with DOCTYPEs and markup. As many people have told me, I had an error I didn’t notice in the original HTML. I forgot to close the SELECT tag. I’ll leave the post as-is.

Not only that, the DOCTYPE is not specified in the document, which causes IE to render in Quirks mode, not standards mode. So I guess the bug is in IE 7 rendering.

So this is a case of PEBKAC, the bug is in the HTML, not the browser.

Here’s an example of the HTML with the SELECT tag properly closed and the proper DOCTYPE. It renders correctly in FireFox 3 and IE 8.

In testing our helper methods for rendering a <select /> element which has some styling applied to it if the element has a validation error, a developer on the MVC team found an interesting bug in how browsers render a select element with a border applied to it via CSS. Check out the following HTML.

<html>
<head>
<style type="text/css">
select {
border: 1px solid #ff0000;
color: #ff0000;
}
</style>
</head>
<body>
 
<select >
  <option>A</option>
<select>
 
</body>
</html>

Here’s how IE 8 renders it. Notice there’s no border. UPDATE: According to people on twitter, this is because I left out the doctype, so IE8 rendered it in old quirks mode, not standards mode.

 ie8-select

Here’s Firefox 3. There’s a border, but there’s two drop-down arrows.

select-ff

Here’s Google Chrome, which gets it right. Since Google Chrome uses the Safari Webkit rendering engine, I believe Safari gets it right as well. I didn’t test it personally, but Opera gets it right too.

select-chrome

Now if you add the following meta tag to the <head /> section of the HTML.

<meta http-equiv='X-UA-Compatible' content='IE=8'>

IE 8 now renders correctly.

select-ie8-meta

You can see for yourself by pointing your browser to an example with the meta tag and without it.