Opinionated ASP.NET MVC by Joshua Flanagan

Joshua Flanagan, who’s been tearing it up for the team I work on at Dovetail, has recently posted two great posts on how we’re doing ASP.NET MVC. I suggest you check ‘em out:

He’s done a good job of trying to wrap everything up in a nice package. Unfortunately some of the guts of how the controllers are found and how the actions are invoked are not that easy to wrap up into a blog post, but he’s done a good job of covering the gist of it all.

Some of our major design goals were/are:

  • Simplify the intake of parameters to actions: One model object with all the properties you need (and maybe more optional ones).  Use a fancy converter that’s smarter than the baseline parameter converter in ASP.NET MVC Preview 3. (NOTE: In Preview 5 there’s the IModelBinder stuff which looks like it may do everything we were doing in Preview 3, we may switch to this but we haven’t looked at it enough yet to determine)
  • In the controllers, separate the preparation of the view from the actual execution of the view (one model in, one model out). Use the action invoker to determine the appropriate thing to do with the results of the controller action invocation*.
  • Use a convention for form element names so that they can easily be picked up on the way back up to the server in a form-post scenario. This is also useful for a modal popup scheme we have which allowed us to develop a “select an existing or create a new” child feature in a fraction of the time it would’ve taken otherwise.
  • The naming thing also helps us with our UI testing as the test harnesses know how to find elements intuitively
  • No magic strings (for reflection-type stuff, anyhow). Anywhere. This means you!  Use static reflection lambda expression (i.e. model => model.SomePropertyName) wherever possible. We declare our textboxes like this:
    <%= this.TextBoxFor( m => m.Contact.FirstName ).Width(200).WithLabel(“FIRST_NAME”) %>   
    Yes, I know FIRST_NAME is a string, but it’s a key to a localization thingee that spits out the correct string based on your language/culture settings.
  • Route all URL generation through an “Invocation Registry” that knows about all the controllers, actions, and their input and output model objects and can determine the correct URL for them and the best way to invoke the actions. No URL’s in the views (well, except maybe for static content).
  • There’s a bunch more that I’m not remembering right now

* By doing this, we can invoke an action via HTTP GET, JSON, in a modal pop-up window, or a number of other ways and the action invoker does the responsible thing with the result model and can return it back to the client using the appropriate mechanism (JSON in, JSON out, etc). The controller never has to know the difference.

Our controller tests are pretty easy to write now, as Josh pointed out. They could be better, but we’re getting there.  We’re still struggling with View testing and are probably going to end up just going with full UI-only testing (key events, button clicks, that sort of thing).  ASP.NET affords a lot of great functionality for the views, but kills a lot of our testing capabilities. It’s tempting to switch to NVelocity or something, but we’d be giving up too much on the functionality side, I’m afraid.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Chad Myers

Chad Myers is the Director of Development for Dovetail Software, in Austin, TX, where he leads a premiere software team building complex enterprise software products. Chad is a .NET software developer specializing in enterprise software designs and architectures. He has over 12 years of software development experience and a proven track record of Agile, test-driven project leadership using both Microsoft and open source tools. He is a community leader who speaks at the Austin .NET User's Group, the ADNUG Code Camp, and participates in various development communities and open source projects.
This entry was posted in ASP.NET, MVC. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Joe

    How do you handle the JSON in, JSON out actions? Are you setting the request header on the client and then the ActionInvoker looks for that header key then returns a JSONResult?

  • http://chadmyers.lostechies.com Chad Myers

    @Joe: Essentially yes. We’re consistent about setting the content type for JSON requests (actually, jQuery is :))

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    Psst. We need to switch:

    < %= this.TextBoxFor( m => m.Contact.FirstName ).Width(200).WithLabel(“FIRST_NAME”) %>

    to

    < %= this.TextBoxFor( m => m.Contact.FirstName ).Width(200).WithLabel() %>

    because LocalizationManager “knows” how to retrieve a localized string title for a class property.

    And no, I don’t see us switching to the IModelBinder stuff in v5. I think our solution is still better than IModelBinder.

    @Joe,

    Part of the “Invocation Registry” (horrific name that I’m to blame for) infrastructure is a Fluent Interface for hooking up controller actions and views. We have a grammar for registering controller actions as JSON output in that FI.

  • http://ferventcoder.com Robz

    As opposed to indifferent ASP.NET MVC? Or how about indecisive ASP.NET MVC? That’s the one that brings down your server because it can’t make up it’s mind.

    ;D

  • http://colinjack.blogspot.com Colin Jack

    Great stuff, sounds very interesting and I love the removal of strings from Views/Tests and so on…very useful.

    I wanted to ask about a few bits I still don’t get:

    1) Invocation registry – Do you mean you handle all routing yourself?
    2) LocalizationManager – Do you just key on the object hierarchy to get the descriptions, just interested in how your handling this to be honest.

    I’d also be interested in reading more about how you seperate the preparation of the view from the actual execution of the view. Sounds cool, so far I don’t think I’ve met situations where I’ve needed such a capability but I could be wrong.

  • http://chadmyers.lostechies.com Chad Myers

    1.) No. The request hits the routing engine first and if it determines it’s a straight /controller/action call, it hits the InvocationRegistry and it takes off from there. We still have the ability to do crazy routes in the routing engine.

    2.) No, we have keys we ‘key’ on. Also, we can have it use properties of a given entity for things like grid header rows and such. The localization manager will look for the localized version of the “FirstName” property of the “Contact” object, for example.

    3.) Getting the “choose the view” responsibility out of the controller helps keep our controllers nice and lean and reusable for multiple purposes. It allows us to use the same controller for serving up a normal page, or the same functionality in a modal pop-up. The invocation stuff handles whether it’s in a modal pop-up (and the associated callback), or if it’s in a full page (normal mode).

  • http://colinjack.blogspot.com Colin Jack

    Ta for the reply, on 2 though I’m not following. If the localization manager looks for the localized version of “FirstName” property of the “Contact” object then isn’t that keying on the class hierarchy.

    Only care because localization is something I’ve done a few times before and will have to work on again soon and I *hate* it.

  • http://chadmyers.lostechies.com Chad Myers

    @Colin:

    Sorry, I wasn’t very clear. We can localize strings based on a straight key (i.e. “USERNAME_KEY”) or, when we’re doing something based on reflection, like a textbox with a label, it will default to using a localized string keyed off the property.

    // Inferred localized string label for textbox
    < %= this.TextBoxFor(m=>m.Case.Title) %>

    // Explicit localized string labelf or textbox
    < %= this.TextBoxFor(m=>m.Case.Title).WithLabel(“CASE_TITLE_KEY”) %>