The ASP.NET MVC ActionController – The controllerless action, or actionless controller

There has been quite  a bit of discussion about how controllers are really namespaces trying to get out once you use the concept on a nontrivial application. 

Brian Donahue’s post on The anti-controller revolution prompted me to do this little experiment.  He references some twitter posts by Jimmy Bogard, one of my esteemed consultants at Headspring SystemsChad Myers also has opined about the notion of more independent actions and has cited precedence.

My interest in this space is purely practical.  I really don’t care how patterns are published.  I don’t care about “being true” to the MVC pattern or any other pattern.  I’m more interested in being effective with web applications on .Net.  After having experience with MvcContrib, CodeCampServer, and a much larger ASP.NET MVC implementation (200+ screens), I have come to see how controllers end up searching for an identity.  What is a ProductController anyway?  That’s just about as specific as classes called ProductService, ProductManager, ProductUtility, etc. 

Overview

You can SVN checkout the following url to see my spike code:  https://palermo.googlecode.com/svn/actioncontroller/trunk.  You can get it in zip file format here. (I repeat these links below)

In the default ASP.NET MVC project template, there is a HomeController, and then there is an AccountController that hooks up the ASP.NET MembershipProvider.  The AccountController does registering, logging in/out, changing password, and it is WAY too big.  The AccountController lacks cohesion.  The AccountController has more than one reason to change.  Each of the actions seem like they are more cohesive.

I’m going to narrate a before and an after of the ASP.NET MVC default project as I refactored it into being a ActionController-based application.  The controller names were promoted to namespaces and the action names were promoted to controller names.  The requests for a GET and POST of the same url are handled by the same ActionController since the action name is the same.  There are two methods in the class; one that handles GET and one that handles POST.  Within the ActionController, the methods are named “execute” since the name of the action is in the class name.  View structure stayed the same.  Seems there isn’t much pain in the view structure.

Routes

Let’s look at the possible URLs in the default project:

  • / (GET)
  • /Home/About (GET)
  • /Account/LogOn (GET)
  • /Account/LogOn (POST)
  • /Account/Register (GET)
  • /Account/Register (POST)
  • /Account/ChangePassword (GET)
  • /Account/ChangePassword (POST)
  • /Account/ChangePasswordSuccess (GET)

Throughout my refactoring, the urls do not change.  The routes do not change.  The only thing that changes is that the controllers are broken up into multiple classes along action lines.  For instance, There are two LogOn actions.  One for the form rendering, and one to accept the post.  These two are cohesive together, but they are not cohesive when combined with register, like they are by default with the AccountController.

Before

image  Let’s start at the beginning.  To the top (MvcApplication2) is the default project with no modification.  You can check out the code yourself.  The HomeController is pretty easy to dissect, but the Account controller is responsible for 7 independent requests.  5 too many, I think. 

After

imageTo the top(MvcApplication1), we have what the project ended up looking like after the refactoring.

You can see that the actions from the AccountController were promoted to be controllers. 

Let’s take a look at the LogOnController.  I have pushed the two Execute methods to the top for clarity.  With ActionControllers, the controller is only concerned about one action.  In this case, the GET pass of the action renders a form, and the POST pass of the action modifies some server state.  Here is the code:

Sample ActionController

namespace MvcApplication1.Controllers.Account
{
public class LogOnController : ActionController
{
public ActionResult Execute()
{

return View();
}



[AcceptVerbs(HttpVerbs.Post)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "Needs to take same parameter type as Controller.Redirect()")]
public ActionResult Execute(string userName, string password, bool rememberMe, string returnUrl)
{

if (!ValidateLogOn(userName, password))
{
return View();
}

FormsAuth.SignIn(userName, rememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("", "Home");
}
}



public LogOnController()
: this(null, null)
{
}

// This constructor is not used by the MVC framework but is instead provided for ease
// of unit testing this type. See the comments at the end of this file for more
// information.

public LogOnController(IFormsAuthentication formsAuth, IMembershipService service)
{
FormsAuth = formsAuth ?? new FormsAuthenticationService();
MembershipService = service ?? new AccountMembershipService();
}

public IFormsAuthentication FormsAuth
{
get;
private set;
}

public IMembershipService MembershipService
{
get;
private set;
}

private bool ValidateLogOn(string userName, string password)
{
if (String.IsNullOrEmpty(userName))
{
ModelState.AddModelError("username", "You must specify a username.");
}
if (String.IsNullOrEmpty(password))
{
ModelState.AddModelError("password", "You must specify a password.");
}
if (!MembershipService.ValidateUser(userName, password))
{
ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
}

return ModelState.IsValid;
}

}
}


What’s at the heart of this, you might think?  There are two things:
  1. A Custom controller factory (to find the right ActionController)
  2. A controller base class, “ActionController”

Download

I spiked out an implementation of the ActionController.  This is completely non-vetted in a real environment, but the sample project is available for download here. (http://palermo.googlecode.com/files/ActionController.zip)  This includes an ASP.NET MVC project with the ActionController class.  You can SVN co here:  https://palermo.googlecode.com/svn/actioncontroller/trunk

Please download the code and check it out.  What it ends up doing for the actions is groups them cohesively and then the concept of the “controller” becomes a namespace.  The controller factory needs work to be able to locate ActionControllers that are unique within the controller namespace but not unique throughout the project.  This is a rough first pass that I did in 30 minutes.

I’m not sure if this is what I’ll commit to MvcContrib for more widespread consumption, but I (and my teams) are feeling a bit of pain with bloated controllers, so it’s worth considering.  What I like most about this approach is that the only thing that changed was the controllers.  The routes don’t change.  The view folder structure doesn’t change.  The Html helpers don’t change.  We merely refer to the concept of a controller as a namespace rather than a class.  We now refer to an action as a class instead of a method.


Trackbacks

What's in a controller? That which we call an action Posted on 6.20.2009 at 11:58 PM

What's in a controller? That which we call an action

Daily tech links for .net and related technologies - June 19-23, 2009 Posted on 6.23.2009 at 12:05 AM

Daily tech links for .net and related technologies - June 19-23, 2009 Web Development Rapid Application

Controller Actions Revisited « Michael Carman Posted on 7.09.2009 at 4:47 PM

Pingback from Controller Actions Revisited « Michael Carman

RESTify – Extending ASP.NET MVC Posted on 8.24.2009 at 6:41 PM

Recently there has been a talk of Actionless Controller in ASP.NET MVC space. Basically we are doing

RESTify ??? Extending ASP.NET MVC | I love .NET! Posted on 8.24.2009 at 7:22 PM

Pingback from RESTify ??? Extending ASP.NET MVC | I love .NET!

The ViewModel and the MasterPage « typesafe Posted on 11.01.2009 at 1:57 PM

Pingback from The ViewModel and the MasterPage « typesafe

Comments

Christian said on 6.20.2009 at 12:49 AM

hi jeffrey,

i really like this approach. we were also using ASP.Net MVC for a big solution and experienced the same pains. it's hard to keep the controllers short!

i think we are getting closer to a really good solution, if we combine this concept, the concept of "areas" and the concept of T4 templating (as described by david ebbo)

Jeremy Skinner said on 6.20.2009 at 3:34 AM

I really like this approach. I've actually been working on something very similar over the last couple of days (which I'll blog about it shortly), the difference being the action classes can be POCO and a different method can be invoked based on the http verb (so you could have a Get method and a Post method for example).

webdevvote.com said on 6.20.2009 at 10:00 AM

You are voted!

Track back from WebDevVote.com

Dennis Leng said on 6.22.2009 at 4:07 AM

hi Jeffrey

what a brilliant approach (SRP), combine it with Ninject would be even better. we could have the accountactioncontroller which has all the account related services, which could be ninjected as setting them up as properties. Logoncontroller, logoffcontroller etc inherit from it.something like :

public class AccountActionController : ActionController

{

[inject]

public IFormsAuthentication FormsAuth {get;set;}

}

public class LogOnController : AccountActionController

{

.............

}

Cheers

Andrew Gunn said on 6.22.2009 at 8:54 AM

Greate post, I live this idea. One question though... can a single projcet contain controllers structured in the traditional way AND your way? I tried added ActionController and ActionControllerFactory to an existing project and it broke all teh traditional controllers - the URLs simply return 404s.

I know it's something to do with the ActionControllerFactory and adding it via the Global file.

Jeffrey Palermo said on 6.22.2009 at 8:57 AM

@Andrew,

First, please do not add this spike code to a real project. It is untested and only works on the sample. It's an idea, not something I use or would recommend using. To get this to production-ready would require quite a bit more work.

Please don't risk a real project with this code.

That being said, the controller factory would have to be rewritten to support a mix. I've imagined how it's possible (it is), but the controller factory would have to search first for a regular controller, and if it doesn't find a match, then search for an action controller.

Brendan Enrick said on 6.22.2009 at 9:04 AM

Good post. Yeah, that AccountController that comes with the default MVC app has always bugged me as well.

The only thing that bugs me with your fix is the ChangePasswordSuccessController. I would normally let that go in the ChangePasswordController. I try to keep things separate, but that one feels like one too many controllers to me.

Jan Willem said on 6.22.2009 at 10:04 AM

Does this solution imply that you can only have one IndexController in the project?

Since the ControllerFactory only looks at the action:

return base.GetControllerType(action);

Sean Chambers said on 6.22.2009 at 10:10 AM

I'm sorry but this approach doesn't make any sense to me.

Your controllers should be arranged around "resources" so that the required verbs are available on one controller. Slicing controller responsibilities in this manner is a bad idea IMO. I understand that you're trying to attack SRP here, but I think you're barking up the wrong tree.

What Jimmy and Chad were talking about was from a lower level, aka. when the request is processed and dispatched.

The concept you have here is letting that seperation bleed out into your controllers.

I personally wouldn't recommend for anyone to take this approach as this is slicing the controllers in the wrong place.

Jeffrey Palermo said on 6.22.2009 at 10:59 AM

@Jan,

With the current naive implementation, there can only be one IndexController. For this to really work, the controller factory would have to be developed to handle all the necessary cases.

Jeffrey Palermo said on 6.22.2009 at 11:01 AM

@Sean Chambers,

>>I'm sorry but this approach doesn't make any sense to me.

To tell you the truth, I'm not sure if it makes sense to me either. I knew throwing out the idea would get some creative juices going. Writing the post forced me to think it through and create a sample implementation.

It's pretty much a hack of the controller base class right now, and I'm not sure I like it.

Brian Donahue said on 6.22.2009 at 12:28 PM

Hi Jeffrey,

Thanks for the follow-up post. One action per controller would help some of the issues we're discussing, but it seems like treating the symptoms and not the root issue. I know this was just a quick spike on the idea, and it is something that can be done without much effort using the existing MVC framework.

The idea that appeals to me is to do away with the concept of multiple controllers, and have a single FrontController that can find the right command or commands to handle a request. Chad Myers does a great job describing it in his post: http://is.gd/19p1F

I think that inheriting from Controller and keeping the Controller/Action MVC nomenclature could make things more confusing than necessary.

Marcel du Preez said on 6.23.2009 at 4:46 AM

I agree with Sean

the AccountController example could confuse if you use it as your basis for building a MVC app.

1 Model = 1 Controller with actions to manipulate the Model. Model could be a very simple class or a logical aggregation of classes.

If your controllers are getting to big, then there is an underlying problem

Chris Pietschmann said on 6.23.2009 at 9:25 PM

This is a neat idea in concept. This sample proves it "possible", but does seem to be a pretty hackish way; it would definitely need to be refined further to be production ready.

Also, another thought is, Why not just use Partial Classes to build the Controllers using multiple code files? You would get the same breakup of "an action per file" as this sample.

Jeffrey Palermo said on 6.23.2009 at 9:28 PM

@Chris,

Partial classes wouldn't work because you still have to have a single constructor. If 6 actions need 6 dependencies, you still have a constructor with 6 dependencies.

Partial classes are only lipstick on a fat pig of a class. Same as regions, just hiding the bloat.

This is very hackish. :-). I hacked it out in 30 minutes.

Brian said on 6.24.2009 at 11:49 AM

I won't pretend to be a ASP.NET MVC expert - more of a novice if anything; but it seems like in the refactor you now have an explosion of classes. Is more classes necessarily better than less larger classes? For new team members wouldn't it be harder to grasp all these extra moving parts versus just a couple of meaty ones? To me this line of thought leads to a 1:1 relationship of controller with use case which could be a large number and generalization is lost.

But again, I'm nothing but a novice when it comes to ASP.NET MVC, so if I'm way off I don't mind being told so; but this is kind of what we saw when working with Microsoft P&P UIP when we over specified our controllers to specific tasks. Ultimately though, this all leads to my second question: In your book (that I'm considering buying), has it been updated with all the new things that have been introduced since it's posted date of Feb. 2008?

Thanks!

Adam said on 6.24.2009 at 10:38 PM

Love your work Jeff and really like the idea. It makes a lot more sense mapping to actions. The controller could even been grouped by folders, but namespaces seems good also.

Would love to see more samples on this, as well as the Front Controller proposed by Chad.

Cheers.

Neal Blomfield said on 7.05.2009 at 8:31 PM

What if you left controllers and actions as they are but instead of using DTOs, Domain Objects and primitives as parameters, you used command objects that contained properties representing the required command parameters? Using this approach, the url becomes an entry point for a number of independent commands that the action simply executes and maps the results into "something" that the view can use.

This approach would greatly simplify most scenarios as the controller would be reduced to controlling application flow rather than participating in the application logic directly.

Additional benefit is gained with complex views where many independent items of information are required - use multiple discrete commands as parameters. For example, you can imagine the ugliness inside a controller action where the view requires you to search for products meeting some criteria as well as display the users login status and their shopping cart details. However if you take a command based approach the action becomes something like:

ProductController.Search( ProductSearchCommand search, LoginStatusCommand loginStatus, ShoppingCartDetailsCommand shoppingCart).

Does this make sense as a means to end the madness inside controllers?

Jeffrey Palermo said on 7.06.2009 at 9:01 AM

@Neal,

I'm not really following this approach or the "madness inside controllers". I think controller are pretty simple until you get to very large applications. With large applications, there are always additional patterns to deal with that type of complexity.

Neal Blomfield said on 7.06.2009 at 8:49 PM

The "madness inside controllers" I was referring to is the huge number of dependencies you often see in any controller that is noun focussed ( i.e. UserController ) and has a reasonable level of complexity. It appeared as this was one of the reasons you considered creating controllerless actions.

The following example is from an app I am currently building. the spec is essentially "As a potential user, I want to register, so that I can use the application".

Acceptance criteria are reasonably straight forward - need to collect various details, all of which are required and create an Account, a User, make the user the account owner, mark the account and user as inactive pending activation, send an activation email, and display a page based on the outcome of everything.

Originally I had almost all of this logic in the controller - 50+ lines of validation and domain manipulation with some notification and navigation path logic thrown in.

Now I have an action that looks like:

public ActionResult Index( IRegistrationCommand registration )

{

var registrationResult = registration.Execute();

if (registrationResult.IsSuccessful)

{

return View( "PendingActivation" );

}

foreach( var error in registrationResult.Errors )

{

ModelState.AddModelError( error.Key, error.Value );

}

return View();

}

The IRegistrationCommand implementation manages all of the validation and domain manipulation (this action is currently missing the email notification logic), and all the controller manages is navigation paths and view data.

For completeness - here is the current IRegistrationCommand implementation:

public class RegistrationCommand : IRegistrationCommand

{

private readonly IAccountRepository _accountRepository;

private readonly IValidator<RegistrationCommand> _validator;

public string AccountName { get; set; }

public string Username { get; set; }

public string Passphrase { get; set; }

public string ConfirmPassphrase { get; set; }

public string Name { get; set; }

public string EmailAddress { get; set; }

public string Country { get; set; }

public string Timezone { get; set; }

public bool AcceptTermsAndConditions { get; set; }

public RegistrationCommand( IAccountRepository accountRepository, IValidator<RegistrationCommand> validator )

{

_accountRepository = accountRepository;

_validator = validator;

}

public RegistrationResult Execute()

{

var validationErrors = Validate();

if( validationErrors.Count > 0 )

{

return new RegistrationResult( validationErrors );

}

var user = new User( Username, Passphrase, Name, EmailAddress, Country, Timezone, UserStatus.InactivePendingActivation );

var account = new Account(AccountName, user, AccountStatus.InactivePendingActivation);

_accountRepository.Save( account );

return new RegistrationResult( account );

}

private IDictionary<string, string> Validate()

{

return _validator.Validate( this );

}

}

While this is a simple scenario, it easily scales to more complex views where you need to manage multiple disparate actions for one request. A classic example of this would be searching searching for a product on an ecommerce site - you need to search for the product, display the users login status and display the contents of their shopping cart. A common implementation of an action for this would require a controller with dependencies on a product repository, a shopping cart repository and some kind of authentication / authorisation facade and the action would be responsible for g

Jeffrey Palermo said on 7.07.2009 at 8:58 AM

@Neal,

Ah. I understand. Very good approach. It keeps the application logic away from the application flow. I can see this very useful. Another natural progression as the application grows is the need to pull away user input from the operation which acts on that user input. That ends up dividing your command objects by pulling a message object out of the command and having the command consume and operate on the message object.

Thanks for the comment!

SanjayU said on 7.13.2009 at 9:09 AM

Neil,

Definitely clean approach - have you used this or similar techniques in a production application?

Paul said on 10.23.2009 at 6:55 PM

Funny, I took a slightly different approach to do the same thing. Scan for controllers with a given base type and the namespace/class name becomes the url with HttpMethod being the action. Great for Ajax. Been loving it so far, though nothing close to production.

Matthew Zalewski said on 11.24.2009 at 4:40 AM

After reading this post, I started thinking about how WF could be used to create actions instead. I've posted an example at synaptixdevelopment.blogspot.com/.../aspnet-mvc-and-

I'm interested in any thoughts/suggestions you may have.