Dynamic Actions in MVC 3

In MVC 3 you get a lot of stuff for free. Convention determines a lot of the basic stuff. This is very awesome and helps alleviate some of the more mundane and boring work involved with setting up an ASP.NET site. However, with magic comes limitations. In my particular case I need to have an action with an arbitrary number of parameters without the need to have an overload in my controller for each parameter. Lets look at what I’m talking about.

My example is a bit of a stretch, but should help explain what I’m doing. If I have a controller named ContactsController with an action named Find which takes one parameter which is a name, then I could call http://site/Contacts/Find/Jon and this would return the contact with the name Jon. However, if I want an action named CreateGroup which will take in a list of names which will then create a group including all of the people whose names were passed in, I’d need to do http://site/Contacts/CreateGroup/Jon/Holly/Drew/Brian/. What I will be addressing in this blog is how to do this without the need for an overload of CreateGroup for 2, 3, 4, etc. names as parameters.

In the Global.asax file we will need to map the correct routes. If we use the MVC 3 Internet Template in VS2010 then we’ll have the following default route:

routes.MapRoute(
	"Default", // Route name
	"{controller}/{action}/{id}", // URL with parameters
	new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

This default route handles the Find action just fine, however it will not work with CreateGroup. We need a way to specify an unlimited number of parameters. In order to accomplish this we need the following route:

//NOTE: This prevents creating routes after this route that have a static number of parameters
routes.MapRoute(
	"DynamicActionRoute",
	"{controller}/{action}/{*rawParams}"
);

What we did here is say that for any given Controller and any given Action use the no parameter Action regardless of how many parameters there are. So in our controller we will now use:

public class ContactsController : Controller
{
	public ActionResult Find(string id)
	{
		return View();
	}

    public ActionResult CreateGroup(string rawParams)
	{
		var names = rawParams.Split('/');
		//TODO: process names
		return View();
	}
}

As long as the name of the parameter in the route matches the name of the parameter in the Action we will receive our string of names. After we parse it out we’ll be good to go!

Some Words of Caution

When doing this you cannot have an overloaded Action named CreateGroup. If CreateGroup is overloaded you will get the following error of ambiguity:

The current request for action ‘CreateGroup’ on controller type ‘ContactsController’ is ambiguous between the following action methods:
System.Web.Mvc.ActionResult CreateGroup(System.String) on type Mvc3RazorPoC.Controllers.ContactsController
System.Web.Mvc.ActionResult CreateGroup() on type Mvc3RazorPoC.Controllers.ContactsController

This makes sense in our situation, because we always want to handle all parameters in this one Action.

Also, this should be the last route in the Global.asax file. It will prevent overloaded routes from being fired correctly. If you have an Action that takes in 2 parameters and the route is placed after our dynamic route the dynamic route will kick in and the parameters will not be filled correctly even though the Action will be called correctly. If the 2 parameter routed is above the dynamic routed all will be well and work as expected.

One thought on “Dynamic Actions in MVC 3

  1. I have found that it is far more manageable in large MVC projects to specify the controller for special routing rules like these. It makes it more manageable because it won’t break other routes with specific behaviors and then this only needs to be placed after the default route and after any other routes for that controller.

    We have also found that creating a static method called “createRoutes” in each of our controllers to be helpful. These methods take in the routeCollection and apply all the controller specific routes needed. We then call these methods from the global.ashx to bind the routes. This provides the added advantage of having the routes visible to you while in the controller class you are working on and not having to switch between files.

Leave a Reply

Your email address will not be published. Required fields are marked *