Ok this has been asked before but there is no solid solution out there. So for purpose of myself and others who may find this useful.
In MVC2 (ASP.NET) I want it so when someone navigates to the website, there is a default area specified. So navigating to my site should send you to ControllerX ActionY in AreaZ.
Using the following route in the Global.asax
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
);
Now this works as in it does try to serve the correct page. However MVC proceeds to look for the View in the root of the site and not in the Area folder.
Is there a way to resolve this?
EDIT
There is a 'Solution' and that is in ControllerX, ActionY return the full path of the view. Bit of a hack but it does work. However I'm hoping there is a better solution.
public ActionResult ActionY()
{
return View("~/Areas/AreaZ/views/ActionY.aspx");
}
Edit:
This also becomes an issue when having a HTML ActionLink of the page. If the area is not set the Action Link is output blank.
Is all of this by design or a flaw?
Best Answer
This one interested me, and I finally had a chance to look into it. Other folks apparently haven't understood that this is an issue with finding the view, not an issue with the routing itself - and that's probably because your question title indicates that it's about routing.
In any case, because this is a View-related issue, the only way to get what you want is to override the default view engine. Normally, when you do this, it's for the simple purpose of switching your view engine (i.e. to Spark, NHaml, etc.). In this case, it's not the View-creation logic we need to override, but the
FindPartialView
andFindView
methods in theVirtualPathProviderViewEngine
class.You can thank your lucky stars that these methods are in fact virtual, because everything else in the
VirtualPathProviderViewEngine
is not even accessible - it's private, and that makes it very annoying to override the find logic because you have to basically rewrite half of the code that's already been written if you want it to play nice with the location cache and the location formats. After some digging in Reflector I finally managed to come up with a working solution.What I've done here is to first create an abstract
AreaAwareViewEngine
that derives directly fromVirtualPathProviderViewEngine
instead ofWebFormViewEngine
. I did this so that if you want to create Spark views instead (or whatever), you can still use this class as the base type.The code below is pretty long-winded, so to give you a quick summary of what it actually does: It lets you put a
{2}
into the location format, which corresponds to the area name, the same way{1}
corresponds to the controller name. That's it! That's what we had to write all this code for:BaseAreaAwareViewEngine.cs
Now as stated, this isn't a concrete engine, so you have to create that as well. This part, fortunately, is much easier, all we need to do is set the default formats and actually create the views:
AreaAwareViewEngine.cs
Note that we've added few entries to the standard
ViewLocationFormats
. These are the new{2}
entries, where the{2}
will be mapped to thearea
we put in theRouteData
. I've left theMasterLocationFormats
alone, but obviously you can change that if you want.Now modify your
global.asax
to register this view engine:Global.asax.cs
...and register the default route:
Now Create the
AreaController
we just referenced:DefaultController.cs (in ~/Controllers/)
Obviously we need the directory structure and view to go with it - we'll keep this super simple:
TestView.aspx (in ~/Areas/AreaZ/Views/Default/ or ~/Areas/AreaZ/Views/Shared/)
And that's it. Finally, we're done.
For the most part, you should be able to just take the
BaseAreaAwareViewEngine
andAreaAwareViewEngine
and drop it into any MVC project, so even though it took a lot of code to get this done, you only have to write it once. After that, it's just a matter of editing a few lines inglobal.asax.cs
and creating your site structure.