One of the new features in ASP.NET MVC2 is support for areas – a way to organize large projects and group code for specific sections together. For example, instead of having ~/Controllers/HomeController.cs and all the related views in ~/Views/Home/, you have ~/Areas/Home/Controllers/HomeController.cs and the related views in ~/Areas/Home/Views. This might not seem like a big deal, but the larger your project gets the more you come to appreciate the little things like this.
While this new feature is part of ASP.NET MVC2, developers using MVC1 are not out of luck; with a little bit of code (most of which has already been written for you) you can achieve the same effect. The first thing you need to do is create the folder structure. Since MVC2 uses the root folder ‘Areas’, I will too. Inside this folder, create a sub-folder for each of your areas; for this example I chose to create a folder for ‘User’ and ‘Admin’. Inside each of your area folders, you will need to create folders for ‘Views’ and ‘Controllers’. Note that you can create a models directory if you want, but you should consider putting your models in a separate solution.
Lets start with the controllers. MVC doesn’t care where you place controllers – they don’t even have to be part of the project (the can be part of a referenced assembly)! One problem you might run into, however, is creating multiple controllers with the same name. For example, both our User and Admin areas will have a HomeController. You should put the controllers in different namespaces, like so:
Mvc1Areas is the base namespace used in my example solution, Mvc1Areas. All controllers go in Mvc1Areas.Controllers.{AreaName}. Following this pattern, the controller for the User area is in the namespace Mvc1Areas.Controllers.User. When you create your routes, you can tell the MVC framework what namespace to look in; we will take a look at the routing configuration after views.
Now create a new MVC View Page in Areas/User/Views/Home named Index.aspx. Unfortunately, the MVC framework will not find it yet. View files are found by the View Engine, which has a list of search paths it will look in to try and find the requested view. If you use the .NET Reflector to inspect System.Web.Mvc.dll, check out WebFormViewEngine:
. This is the default list of locations searched. {1} will be replaced by the controller name, and {2} by the action. We need to edit this list to support another parameter {2} which can be filled in with the area. Unfortunately, this turns out to be a bit of work. Fortunately for you, all that work is already done:
Notice that we added search locations with a ‘{2}’ placeholder, which is filled in with the area name. Add this file anywhere in the project; I put it in the root Views directory. Note that this is not a stand-alone view engine; it is a wrapper around the default WebForms view engine. Now you need to tell the framework to use this view engine instead of the default one; add the following to the end of your RegisterRoutes function:
This removes the default view engine and adds ours.
So we have our view engine that is aware of our views, but how do we tell it what area to look in? And where do the namespaces for our controllers come into play? Take a look at the following code used to register our routes:
You will notice that we are adding an area property in the third parameter. This property is read by our modified view engine and used to determine where to look. Also notice the fourth parameter, which you might not have had need to use before. This parameter lets you specify a list of namespaces to give higher precedence to when choosing the right controller.
And there you have it! The AreaAwareViewEngine is based on code found on a relevant StackOverflow question, so kudos to Aaronaught for laying the foundation for this. You can download an example project. Please leave a comment with any suggestions, feedback, etc etc. I would love to hear from anyone who finds this useful!