ASP.NET MVC - Intercepting the RouteValueDictionary

ASP.NET MVC Preview 3 suffers from a bug where the controller name is omitted from the RouteValueDictionary when you call Html.ActionLink or Url.Action unless you explicitly specify it.

Imagine you have the following routes defined:

routes.Add(new Route("Other/List",
	new RouteValueDictionary(new {
		controller = "Other",
		action = "List"
	}),
	new MvcRouteHandler())
);
 
routes.Add(new Route("{controller}/{action}/{id}",
	new RouteValueDictionary(new {
		action = "Index",
		id = (string)null
	}),
	new MvcRouteHandler())
);

..and you have a HomeController with two actions: Index and List

public class HomeController : Controller {
	public ActionResult Index() {
		return View();
	}
 
	public ActionResult List() {
		return View();
	}
}

And imagine the following code in the Index view:

<%= Url.Action("List") %>

This should generate MyApp/Home/List but instead it generates MyApp/Other/List.

To work around this, you can intercept the RouteValueDictionary before it is passed to your routes by adding a route 'pre-parser'. In here, you can copy the controller name from the routedata into the RouteValuesDictionary.

public class RouteValuePreParser : RouteBase {
	public override RouteData GetRouteData(HttpContextBase httpContext) {
		return null;
	}
 
	public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
		if(!values.ContainsKey("controller") && requestContext.RouteData.Values.ContainsKey("controller")) {
			values.Add("controller", requestContext.RouteData.Values["controller"]);
		}
		return null;
	}
}

You can then add this 'fake' route to your RouteTable, but it must be the first route:

RouteTable.Routes.Add(new RouteValuePreParser());
Written on June 3, 2008