Extending ASP.NET MVC To Add Conventions

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

UPDATE: Much of this post is out-of-date with the latest versions of MVC. We long sinced removed the ControllerAction attribute.

Note: If you hate reading and just want the code, it is at the bottom.

Eons ago, I was a youngster living in Spain watching my Saturday morning cartoons when my dad walked in bearing freshly made taquitos and a small cup of green stuff. The taquitos looked delicious, but I was appalled at the green stuff.

Was this some kind of joke? My dad wanted me to simply just taste it but I refused because I absolutely knew it would suck just by looking at it. The green stuff, of course, was guacamole, which I love by the truckload now.

Guacamole

With all the code samples and blog posts published about the ASP.NET MVC Framework, there’s been some debate about the big picture stuff.

  • Should I be looking to migrate to this? (Depends)
  • Will this replace Web Forms? (NO!)
  • Is this feature even necessary? (I sure think so!)

Interestingly enough, the most passionate debate I’ve seen is not around these big picture questions, but is centered around very specific detailed design decisions. After all, we are software developers, and if there’s one thing software developers love to do, it’s debate the design and architecture of code.

I’m no exception to this rule and admit I rather enjoy it, sometimes to the point of absurdity and pointlessness. At the end of the day, however, the framework developer has to make a decision and move forward so he or she can go home. These choices will never make everyone happy because there is no such thing as a perfect design that satisfies everyone.

That doesn’t mean we give up trying though!

Hopefully the quality of feedback the framework designer receives pushes that designer to reevaluate assumptions and either reinforces the decision, or provides insight for an even better decision.

One design decision in particular that seems to have drawn somewhat disproportionate amount of attention is the decision to require a [ControllerAction] attribute on public methods within a controller class that are meant to be a web visible action. There’s been much discussion in various mailing lists and in some blog posts before the CTP has been released.

This post is not going to rehash these concerns nor address these concerns. If I think it would help, perhaps I will have a follow-up post in which I explain some of the reasoning behind this decision. This would give those who feel all that strongly about this a chance to make a well-reasoned point by point refutation should they wish.

I worry about focusing too much on this one issue. I don’t want it to become such a hang up that it disproportionately dominates discussion and feedback at the expense of gathering valuable feedback on other areas of the framework. There is much more to the framework than this one issue.

At the same time, I do understand this issue is about more than a single attribute. It’s about applying a design philosophy centered around convention over configurationand where it works well and where it doesn’t.

Like guacamole, I hope that critics of this particular issue don’t judge it by sight alone and give it a real honest try. See if it makes as big a difference as you think. Maybe it does. Maybe it doesn’t. At least you’ve tasted it. Feedback based on trying it out for a while is more valuable and potent than feedback based on just on seeing sample code.

Please understand that I’m not dismissing feedback based on what you have seen. It certainly is valuable and much of it has been incorporated and discussed in our design meetings. Some of it has led to changes. All I am saying is that as valuable as that feedback is, feedback based on usage is even more valuable.

The Convention Controller

However, if you still hate it, I have a little workaround for you :). I’ve written a custom controller base class that allows for a more “convention over configuration” approach to building your controllers named ConventionController.

By inheriting from this controller instead of the vanilla Controller class, you no longer need to add the [ControllerAction] attribute to every public method. You also don’t need to call RenderView if you name your view the same name as the action. Of course this means you cannot use strongly typed views and must use the ViewData property bag to pass data to the view.

So instead of writing your controller like this:

public class HomeController : Controller
{
  [ControllerAction]
  public void Index()
  {
    //Your action logic
    RenderView("Index");
  }
}

Using my class you could write it like this

public class HomeController : ConventionController
{
  public void Index()
  {
    //Your action logic
  }
}

The key point in posting this code is to demonstrate how easy it is to override the behavior we bake in with something more to your liking. When you look at the code, you will see it wasn’t rocket science to do what I did. Extending the framework is quite easy.

We may not provide the exact out of box experience that you want, but we do try and give you the tools to control your own destiny with this framework and provide you with the power of choice.

I will look into adding this to the MVC Contrib project started by some community members. In the meanwhile, if you like this approach or style of building controllers, you can either add the ConventionController.cs class to your own project or compile it into a separate assembly and drop it into your own projects.

To get to this class, download the example code.

Tags: ASP.NET MVC , Convention over Configuration , ControllerAction

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

28 responses

  1. Avatar for Tales from the SharpSide
    Tales from the SharpSide December 8th, 2007

    Links from the Sharpside [12-09-07]

  2. Avatar for Aaron Jensen
    Aaron Jensen December 8th, 2007

    Nice Phil. This is another example of why the controller should be broken up into separate services. Wouldn't it be nice if you could just add a different IActionInvoker and IViewRenderer to your container instead of having to use inheritance? :)

  3. Avatar for Haacked
    Haacked December 8th, 2007

    Hi Aaron, we've received this feedback from you and others. We're looking into it. It's too late to change for the CTP, but we'll evaluate it for the future releases.
    I have to say though, since we don't ship with a DI framework (choose your own), using inheritance certainly is a lot easier. I don't have to know what your DI container is, I just post a subclass. Also, he DI approach is a lot more configuration.
    This is not a ding against composition over inheritance nor a ding against your idea, only to say inheritance is a valid choice sometimes. :)

  4. Avatar for Ayende Rahien
    Ayende Rahien December 8th, 2007

    It is good to see that it is easy to do.

  5. Avatar for Aaron Jensen
    Aaron Jensen December 8th, 2007

    > I have to say though, since we don't ship with a DI framework
    > (choose your own), using inheritance certainly is a lot easier. I
    > don't have to know what your DI container is, I just post a
    > subclass. Also, he DI approach is a lot more configuration.
    You can provide the classes and a new implementation of ControllerFactory. No container needed.
    > This is not a ding against composition over inheritance nor a
    > ding against your idea, only to say inheritance is a valid choice
    > sometimes. :)
    Define valid. If by valid you mean it works, then yes, of course. You can do plenty with inheritance. That doesn't mean its the best way. I'm not going to beat a dead horse on the value of composition vs. inheritance, but maybe someone should as it seems a lot of people still don't get it. Putting out a framework that requires per-controller subclassing to make things easily testable... well, is just a bad idea and certainly doesn't show off good practices.

  6. Avatar for eric hexter
    eric hexter December 8th, 2007

    It is good to see the alternative approaches being talked and prototyped. This is a nice sign that shows some your listening. :)

  7. Avatar for David Fauber
    David Fauber December 8th, 2007

    Personally I don't find the attribute thing to be a big deal, and it seems a surprising thing to get bent over. Don't you run into the same sort of thing with AOP in various places? (This is a serious question, not rhetorical)

    "It adds a lot of extra typing for no real benefit."
    I do think this coming from the ALT.NETinati is pretty funny, in line of the "roll eyes" attitude when Mort says the same of DI. (and yes, that is a completely unfair apples and oranges comparison but I still find it hilarious)
    One other thing:
    Controller is going to be in System.Web.Extensions right?

  8. Avatar for Sean Chambers
    Sean Chambers December 8th, 2007

    Phil,
    This post was EXACTLY what I personally was looking for. I must admit that I haven't looked at the framework enough yet and I should have dove deeper before I made a post asking questions. Next time I will be sure to do my research first.
    Now that you have described that this is very easy to accomplish I personally am satisfied. I don't think there is any point of talking about the matter any further. I'm sure once the CTP comes out I'll have many more things to pick at =)
    @David
    This type of thinking and labeling people as part of a particular community is hurting more then it is helping. We really need to get past this mindset if we want to progress and move forward. In addition, I really feel we should not be labeling people as "morts" anymore. This is very counter-productive and doesn't help anyone as a whole. Call me what you will but let's try to keep the sweeping stereotype's to a minimum please. This will help everyone out =)

  9. Avatar for David Fauber
    David Fauber December 8th, 2007

    "Call me what you will..."
    I just used your quote since it was the best one-line representation of what I've perceived as a general attitude, no need to martyr yourself. :)
    It was just something I thought was funny (although completely unfair, as I pointed out), and was trying to use "domain language" to get across my meaning. Anyway, the post-mortem will reveal that I made a low-content post that ultimately did more harm than good. Sorry about that, I plead boredom while waiting on the ctp and now return you to productive discussion.

  10. Avatar for Haacked
    Haacked December 9th, 2007

    Yes please, productive discussion is encouraged. :)
    @David, for the CTP, Controller is in the assembly System.Web.Extensions but the namespace System.Web.Mvc.

  11. Avatar for AgileJoe
    AgileJoe December 9th, 2007

    Yesterday Sean and I talked briefly about Convention over Configuration and how disappointed we both

  12. Avatar for Nick Parker
    Nick Parker December 9th, 2007

    The Pitfalls of Concession

  13. Avatar for Sean Chambers
    Sean Chambers December 9th, 2007

    @David
    I apologize for snapping. Hindsight tells me that I was a little harsh in my response to you.
    To add to the discussion, I am greatly looking forward to the CTP. When is the exact date of the release Phil?

  14. Avatar for Haacked
    Haacked December 9th, 2007

    @Sean I appreciate you being civil and apologizing Sean. It really elevates the level of discourse. It's funny, as we get closer to the CTP, I've noticed better discourse in several forums I'm in. ;)
    As for the exact date, I'm afraid to say anything in case some last minute thing comes up again. How's "sometime soon" for an answer?

  15. Avatar for Christopher Steen
    Christopher Steen December 9th, 2007

    ASP.NET Asp.Net Control For Google Charts [Via: Luke Foust ] Sharepoint Forms based apps with Infopath...

  16. Avatar for Christopher Steen
    Christopher Steen December 9th, 2007

    Link Listing - December 9, 2007

  17. Avatar for Mike Thomas
    Mike Thomas December 9th, 2007

    One way that the framework could make changes like this easier would be to break up the execution flow into more virtual methods. For example add a 'protected virtual GetActionMethod(string actionName)' to the Controller class :)

  18. Avatar for Florian Krüsch
    Florian Krüsch December 9th, 2007

    Yes, it is nice that you have the flexibility to do that. On the other hand, the attribute makes it safe and is consistent with the rest of the framework, which are IMO good reasons to have it there.
    Also, great flexibility and 'convention over configuration' are somewhat contradictive.
    cheers
    Florian

  19. Avatar for AgileJoe
    AgileJoe December 10th, 2007

    I really missed using the [Rescue] attribute from MonoRail in the ASP MVC framework. I missed it so much

  20. Avatar for Roberto Gattinoni
    Roberto Gattinoni December 10th, 2007

    I'll immediately download your Controller class and use it. Having experienced the elegance and conciseness of Rails I must say this is the right choice.

  21. Avatar for Jeffrey Palermo
    Jeffrey Palermo December 13th, 2007

    If anyone here is motivated, we'd love to have this functionality in the MvcContrib open source project.

  22. Avatar for Ayende @ Rahien
    Ayende @ Rahien December 16th, 2007

    On the faults of thinking: It is extensible, the users can add this

  23. Avatar for Francois Tanguay
    Francois Tanguay December 19th, 2007

    Not sure the ConventionController supports RedirectToAction. From what I see:
    class MyController : ConventionController
    {
    public void Foo()
    {
    RedirectToAction("Bar");
    }
    }
    When Foo returns, won't it result in a call to RenderView("Foo")?

  24. Avatar for anonymous
    anonymous March 30th, 2008

    Can someone explain what the big deal behind the requirement to put that attribute above the controller methods is? Is there some valid design aspect that you can make a good point on or is this just the fact that developers are too lazy to put a simple attribute on a method? Which is it here? Is it a matter of just sickening nerd coders that have nothing to do or is there more behind the problem of simply having to include a lousy attribute on a method. I mean someone please explain this "problem" to me cause I don't get it.

  25. Avatar for Dave
    Dave March 30th, 2008

    OK, let me observe.
    I mean really, don't most developers out there have any sort of life? Not even 10%? Go have some "guacamole" and watch a football game and get away from code for once in your life and quit bitching about such stupid sh**. Who cares about this lousy attribute! Just use it and go home and enjoy your life!!! Find a girlfriend, get married, go to a strip bar, start a side business. You only live once...I'm sure there is far better use of your time everyone then debating a simple requirement such as this. If you have that much time, do what Phil says, go and reinvent MVC every weekend. Just use it and maybe improved it but damn, I mean really. I have far better things to do with my time.
    - Dave

  26. Avatar for Haacked
    Haacked March 30th, 2008

    It's a moot point. We ended up removing the attribute.

  27. Avatar for AG
    AG August 4th, 2008

    Phil, How would handle the situation where you want a to do something more intrinsic without compromising the spirit of MVC. For example, lets say you wanted to provide fine grained access to sections of a web page i.e <%= if (IsVisible("sectionname", username))%>. Of course I would want to just access a collection that already had the data i needed (lets call it a dictionary). In the web forms days I would just drop in an HttpModule that would load the data and just access the data (I know barf).

  28. Avatar for haacked
    haacked August 4th, 2008

    I'd use an action filter.