Wednesday, December 30, 2009 4:48 PM
Kazi Manzur Rashid
ASP.NET MVC 2 and why Dynamic Area is not supported
In my previous post, I mentioned that Area is one of the thing that the coming ASP.NET MVC2 needs heavy enhancements (I also created an Issue in CodePlex). The idea was, Area should have dynamic registering/unregistering support instead of registering all the area at the application start. Recently, I was playing with the Area and trying to find out what are the issues (specially road blocks) for adding such support and it turns out the issue is rather with the ASP.NET Routing instead of the ASP.NET MVC Framework itself.
Before getting down to the main discussion, lets us create a very basic area supported ASP.NET MVC application, lets assume that we have an application which has Blog and Forum as Area and our main Index view has the following code:
<h2><%= Html.Encode(ViewData["Message"]) %></h2>
<ul>
<li><%= Html.ActionLink("Blog", "Index", "Home", new { area = "Blog" }, null)%></li>
<li><%= Html.ActionLink("Forum", "Index", "Home", new { area = "Forum" }, null)%></li>
</ul>
Nothing special, just two links which will take you the individual area home. In our global.asax we have the following code:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
If you run the application and click the individual link, it will take you to the corresponding area home as expected. Now, change the code to the following:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
}
We just swapped the lines, now we are registering the application routes first and then the areas. If you run the application and click any of the links, it will throw “Resource Not Found”. If you recall when the Routing was first introduced, you know that ordering of Route registration is very important and this is exactly what happens here, when we are registering the application route first, the ASP.NET Routing treats the Area Name as Controller Name.
So, if we want to add dynamic area registration we have to make sure that the Routes of Area is registered prior the application routes, so I checked the MapRoute of AreaRegistratonContext if I can make an extension method which inserts the Area Routes at the top instead of appending it at the bottom, we can easily add the support for dynamic area registration. The MapRoute of AreaRegistratonContext internally uses the extension method MapRoute of RouteCollection. The following is the MapRoute extension method:
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > 0)) {
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
Check the line 19 which is using the Add with the route name and object. My initial assumption was, since the RouteCollection is Collection type it should have the support for Insert with the name as like as the Add, but it turns out that I am wrong, the RouteCollection does have an Insert but it does not take name as argument and there is no other way to put the name against the route in the RouteCollection. After spending some time with Reflector I find the only important place that it uses the route name is in the non virtual GetVirtualPath method. So if we use the Insert without the name, I think the url generation which depends on the route name will not work. My next try was, what if I created a inherited version of RouteCollection and assign it to the Routes of RouteTable but it does not have any setter, so no luck and could not find any other way to do any kind of monkey patching.
So my request to the ASP.NET Team to add an overloaded version of Insert to RouteCollection which has name as argument in .NET 4.0 and the next service pack for 3.5.
Download : MVCModuleTest.zip
Filed under: Asp.net, MVC, ASPNETMVC, ASP.NET MVC