Single Action Controllers with ASP.Net MVC

While I’ve enjoyed working with the ASP.Net MVC Framework, one thing I wish that it provided is the ability to create controller-less actions. Why you ask? Because I’d rather my controllers only have one reason to change. While this isn’t provided out-of-the-box with ASP.Net MVC, single action controllers can be facilitated pretty easy with ASP.Net Routing.

Let’s say that we have a single CustomerController that has several actions for adding and retrieving customers which we’d like to split up. We could simply move these actions to separate controllers and register routes for each action to maintain the existing URL structure, but this could pose a maintenance issue down the road if you end up with many small controllers. There are some clever things we could do with dependency injection to provide a pretty nice solution, but I’m going to show you a really simple way of achieving this without a lot of extra wiring.

First, create a new class which extends System.Web.Routing.Route. Next, override the GetRouteData() method and paste in the following code:

public override RouteData GetRouteData(HttpContextBase httpContext)
{
	RouteData routeData = base.GetRouteData(httpContext);

	if (routeData != null)
	{
		var controller = routeData.Values["controller"] as string;
		var action = routeData.Values["action"] as string;
		string controllerAction = string.Format("{0}{1}", controller, action);
		routeData.Values["controller"] = controllerAction;
		routeData.Values["action"] = "Execute";	}

	return routeData;
}

Next, define a new route using your new custom Route class. To match the registration API provided by the framework, you can use the following extension method:

public static class RouteCollectionExtensions
{
	public static Route MapActionRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
	{
		var route = new ActionRoute(url, new MvcRouteHandler())
		{
			Defaults = new RouteValueDictionary(defaults),
			Constraints = new RouteValueDictionary(constraints)
		};

		routes.Add(name, route);
		return route;
	}
}

With this in place, the path “~/Customer/Get/1” will route to the following controller:

public class CustomerGetController : Controller
{
	public ActionResult Execute(string id)
	{
		var customer = CustomerRepository.Get(id);
		return View("CustomerView", customer);
	}
}

To see a full working example, I’ve provided a source-only NuGet package here. If you have the NuGet Command Line tool, open up a shell and run the following where you want the source:

>nuget install ActionControllerExampleSource

If you have NuGet.exe in your path, you should be able to just build the solution. Otherwise, you’ll need to edit the batch file in the script directory to set the NuGet path so it can grab a few dependencies it uses. Enjoy.

About Derek Greer

Derek Greer is a consultant, aspiring software craftsman and agile enthusiast currently specializing in C# development on the .Net platform.
This entry was posted in Uncategorized and tagged . Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://twitter.com/fquednau Frank L. Quednau

    Interesting…contrast this to my post in 2009 on wanting to have something rather similar :) (http://realfiction.net/go/151). I’d say, this mechanism of yours is much less intrusive.

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

    You know, this is super easy in FubuMVC and no special routes, and you don’t have to inherit from an abstract base class.

    • http://derekgreer.lostechies.com Derek Greer

      I haven’t had the chance to play with FubuMVC yet, but I’ve heard enough
      about it since its inception to believe there’s a lot to love about the
      design choices you guys have made. I read Joshua Flanagan’s article
      on the topic before posting yesterday and considered mentioning that other
      frameworks took this approach, but decided to just keep the article about
      how this can be achieved with ASP.Net MVC.

  • http://www.larswilhelmsen.com/ Lars Wilhelmsen

    Hi, nice work – but you’ve included the Resharper metadata folder in the Nuget package…

  • http://mutedsolutions.com Derick Bailey

    Can you go a bit deeper into why you want to do this? I’ve heard a number of people talk about it, i know fubumvc supports it, i think openrasta does it by default… but i don’t know why. what’s wrong with having multiple actions in a controller? why does that necessarily mean your controller has more than one reason to change (re: breaks SRP)?

    specific examples, please. :) perhaps another blog post would be good.

    • http://derekgreer.lostechies.com Derek Greer

      Good question, Derick.

      First, I’d like to clarify that I don’t believe having multiple action methods on a single controller is always a SRP violation. Like any other type of class, it depends upon whether the collection of operations is cohesive or not for its particular role within the architecture.

      That said, controllers serve a different role than other types of components (e.g. a domain model). From their origins in Smalltalk-80 to their current manifestations in Web application architecture, controllers are basically message handlers. It can make sense to group messages which interact in a similar way with a given portion of the domain, but when different messages being to require different sets of external dependencies or represent interacting with a single domain model or aggregate root in different ways, this becomes a separate concern.

      To help illustrate, consider yourself as an entity. If we were to create a domain model which completely represented you, we would consider the collective information and behavior to be cohesive from a modeling perspective. Now consider all the ways we can interact with you. We can interact audibly, visually, physically, electronically, etc. and the reasons we would interact with you are innumerable. Should we model all the capabilities of how and why we would interact with you as a single entity? I’m sure you’d agree that we wouldn’t. Of course, an all-encompassing domain model of Derick Bailey would be far more complex than anything we encounter in our normal jobs, but I find viewing issues through extremities helps to amplify the consequences of our design decisions.
      On a much smaller scale, this is how I see controllers. They are the conduits through which we interact with the domain. Just because what we’re interacting with may have multiple capabilities, that doesn’t mean there needs to be a single conduit.

      Regardless of one’s philosophical view of the purpose of the controller, I consider this approach just one tool in the toolbox.

      • http://mutedsolutions.com Derick Bailey

        “it depends” of course. :)

        hmmm…. i had a bunch of examples and ideas to explore and i started typing them out into the comment stream, here. given what you’re saying and what james nail is saying in another comment, i think my samples would lead me down the same path that your talking about.

        i may try to explore my own understand and thoughts in another blog post of my own, to address both what you and james are saying.

        still thinking about this, but i think i have an idea of what you’re getting at.

  • http://profiles.google.com/jrnail23 James Nail

    Hi Derek,
    I also share your frustration with the lack of cohesiveness and SRP-violation pain that the conventional ASP.Net MVC approach seems to push us towards, and I’m just glad you figured out how to make the single-action approach work in ASP.Net MVC before I ended up sinking way too much time into it myself!

    For those who don’t really see the benefit of this approach in comparison to the customary controller approach, I’ll give that explanation a shot:
    I see the issue in part as a reflection of how your app thinks about and works with “data”. I put “data” in quotes, first because I hate using that word anywhere near a discussion about and object oriented domain models, and because I think the typical REST-like (I can’t quite call it RESTful) semantic of MVC controllers are very much reflective of a data-centric, CRUD-oriented approach to application development.

    If you’re happy thinking about your app in CRUD terms (and that’s entirely appropriate for a vast number of web apps out there), you’re probably quite happy with the whole ASP.Net MVC experience — it fits like a glove… but if your app is more behavioral in nature, and want to make a nice task-driven, domain-oriented UI for your app, you might suddenly find yourself beating your head against a wall as you try to shoehorn that approach into the made-for-CRUD semantic of Index/Edit/Update/New/Create/Delete actions.

    Simply, the default ASP.Net MVC approach just seems to make it difficult to express rich, DDD-style domain concepts in the UI. And I obviously can’t speak for everyone, but as a developer, I’d rather be thinking about accurately representing my domain concepts to the user instead of trying to figure out how I fit a complex workflow into the traditional controller design — there’s a sort of impedance mismatch there that throws an unnecessary wrench into the works.

    You can think of controllerless actions as Commands in a command processor design, where all the controller/command class cares about is its one simple, focused, (possibly composable?) responsibility.

    One question, though, with respect to the nuts & bolts of your implementation:
    What does the folder structure of your Views look like? I’m guessing your “CustomerView” view lives in a “Customer” folder, like you’d have with the typical MVC project, but I’d like to confirm that (I suppose I could just stop being lazy and actually take a look at your code).

    Also on the nuts and bolts front, I’d like to know if things like the Post-Redirect-Get pattern would differ substantially (I suspect not).

    Anyway, thanks Derek for a very intriguing post!

    • http://dotnetchris.wordpress.com/ Chris Marisic

      @google-e1aef3bc7cbb476ef2df922fb219c4a6:disqus your comment’s entire premise seems to surround this singular statement

      “but if your app is more behavioral in nature, and want to make a nice
      task-driven, domain-oriented UI for your app, you might suddenly find
      yourself beating your head against a wall”

      I think you need to elaborate on this more because I’ve never had an app where I found myself beating my head against the wall trying to decide what controller an action goes into.

      • http://profiles.google.com/jrnail23 James Nail

        Well, @openid-33918:disqus , here’s one that I had recently…
        Real simple. Click a button that begins a planned sales calling session (loads a CallingSession entity on the server and calls callingSession.StartTimer()).
        OK, so it’s obviously an UPDATE to the existing calling session, but it’s just one of several different updates that could be applied to that session, and I’m not really passing any info to update it with — instead, I’m invoking a behavioral method on the CallingSession entity.
        Basically, I just want to fire off a command to start the session’s timer in this case — I don’t want to modify the name of the session or any details like that.
        So wouldn’t it be much simpler if I had controller actions that worked like Command objects?
        Now I know it’s easy to just make an action called “StartSessionTimer” on my CallingSessionController (which is what I ended up doing, even though it didn’t really fit the RESTful paradigm), but man, I gotta tell you, it didn’t take long for that controller to get REALLY messy.

  • Pingback: DotNetShoutout

  • http://derekgreer.lostechies.com Derek Greer

    I’m not sure I understand your question. Are you asking whether I would use a URL like “/Customer/Get/1″, whether I would use single-action controllers for basic CRUD applications, or something else? Based on your comment, it seems you have some objection to voice, but I’m not quite sure what you’re trying to communicate.

    As far as how best to convince people to use this technique, you’re presumption is that I’m trying to convince anyone. There are many developers who prefer the controller-less action approach to the default ASP.Net MVC approach, or who would appreciate the value in such an approach once exposed to it. My motivation for posting this was to share how I’ve accomplished this with the ASP.Net MVC framework without much fuss for those who care.

  • Hoya Baptiste

    What are the advantages of this approach compared to:

    “Going Controller-less in MVC: The Way Fowler Meant It To Be”
    http://lostechies.com/chadmyers/2009/06/18/going-controller-less-in-mvc-the-way-fowler-meant-it/

    or

    The ASP.NET MVC ActionController – The controllerless action, or actionless controller
    http://jeffreypalermo.com/blog/the-asp-net-mvc-actioncontroller-ndash-the-controllerless-action-or-actionless-controller/

    The concepts behind them (SRP) are quite similar but the implementation is different. I agree that controllers can get “heavy” and I’m looking at ways to simplify this.

    • Anonymous

      The FubuMVC framework provides a more flexible and elegant approach to
      achieving this capability, though the approach presented here is fairly
      similar from a conceptual perspective (i.e. both rely upon custom routing
      conventions for identifying action classes and method names).

      As compared to the approach presented within Jeffery Palermo’s article, this
      approach allows you to configure which URLs you wish to route to action
      classes, requires no custom controller factory configuration, and doesn’t
      require you to inherit from a custom controller base class.

  • http://dotnetchris.wordpress.com/ Chris Marisic

    I would honestly classify this as an anti-pattern of working with MVC. A much better solution is to adopt the Restful Routing paradigm (or create a personal variant for yourself) that controls establish a very concrete convention on the actions the controller will serve like

    ContactsController
    -Index
    -Show
    -Edit

    At this point you have no violations of the SRP. The only reason the Contacts Controller will ever need to change is only if the manipulation of the Contact changes.

    • Anonymous

      Can you explain why you would consider this approach to be an anti-pattern?

      Regarding your example, you haven’t set forth enough context to make any claims about its cohesiveness or superiority to the classic Front Controller pattern.

      • http://dotnetchris.wordpress.com/ Chris Marisic

        I feel it would be an anti-pattern because you’re losing the grouping of logical code blocks. Using the single action controllers to me feels the same as if I’d be designing a system where my domain was setup like

        class Password { public string Password { get; set; }
        class UserName { public string UserName { get; set; }
        class Email { public string Email { get; set; }

        I’d much rather have

        class User { password, username, email }

        • Anonymous

          There have been several questions regarding how this relates to the SRP for controllers, so I may try to do a separate post to explain this topic further.

          In brief, controllers are for accessing the domain, not for modeling the domain. Controllers are essentially message handlers. Their purpose is to translate signals from the user (HTTP requests in the case of Web applications) into interactions with the application. Therefore, the cohesiveness of each controller is measured with respect to its role as a message handler, not with respect to the cohesiveness of the behaviors it interfaces with.

          As a point of pragmatism, I think its fine to group all the message handling which relates to a single model within your application for basic CRUD applications, or even for behavior-rich applications where the technical requirements for handling each of the messages happen to share the same dependencies. That said, encapsulating the handling of each message into its own controller is a strategy you can apply uniformly for both simple and complex applications.

    • http://profiles.google.com/jrnail23 James Nail

      Hi Chris,
      I’d like to hear your take on my comment below with respect to the impedance mismatch between the Restful Routing paradigm and the behavioral nature of a task-based UI.
      I like restful routing, it’s simple and elegant. But for me, it’s been quite painful to try to apply it in the context of an application featuring rich behavior (it’s a really great fit for CRUD, though).

  • Pingback: Cleaning up your MVC controllers « Philly ALT.NET