ASP.NET MVC 2 – Areas Support

ASP.NET MVC 2 introduces the concept of Areas. Areas provide a means of dividing a large web application into multiple projects, each of which can be developed in relative isolation. The goal of this feature is to help manage complexity when developing a large site by factoring the site into multiple projects, which get combined back into the main site before deployment. Despite the multiple projects, it’s all logically one web application.

Where we need this? What is the real world use of this?

First every one will have doubts in understanding areas feature. I want to explain a problem i have faced with one of my projects.

In ASP.NET MVC 1.0, i had a project which has at least 70-80 views. There was an Admin module which has so many sub modules. We had no inbuilt support for group view into Sub views folder inside a main folder.

For example I have a project which has the following folder structure.

/Admin

/Blog

/User

I wanted to create some sub views inside the /Admin main view folder and should be able to access it through URL

just like:

/Admin/Config/MyView  will take view  MyView.aspx from /Admin/Config/MyView.aspx

/Admin/Config/Details   will take view  Details.aspx from /Admin/Config/Details.aspx

and in blogs folder i want to create  a Child View which is called “Posts”, so that i can group all my posts information related views,models and controllers under the /Blogs.

/Blogs/Posts/ViewPosts

But grouping like this or arranging your project is not supported on the ground in ASP.NET MVC 1.0.  ASP.NET MVC 1.0 does not directly supports using Child Controllers and Views, or grouping large projects in different folders. So i am stuck there with my project tasks.

We have some 3rd party or extensions or samples made by some of the Microsoft Solution Experts. They have implemented it by extending the ASP.NET MVC 1.0 classes. Which didn’t go well with my project. Still we are maintaining a 30 or more views in single Controller and more than 1000’s of lines in each controller.

Which was a bad idea. But we had no other options, R&D didn’t go well with it. Deadlines were near, So we went on with current structure. In the future we will make changes to our project, since ASP.NET MVC 2.0 is about to come out soon.  Right now delivery on time is important to us.

I hope you understood what i am trying to explain.

ASP.NET MVC 2 will include built-in support for breaking apart a large MVC application into “areas”.

To quote ScottGu:

Areas provide a means of grouping controllers and views to allow building subsections of a large application in relative isolation to other sections. Each area can be implemented as a separate ASP.NET MVC project which can then be referenced by the main application. This helps manage the complexity when building a large application and facilitates multiple teams working together on a single application together.

There is a detailed walkthrough on MSDN for creating an Areas application using multiple projects. You create a parent project (MvcAreasMultiProject) and two sub-projects (Accounts and Store).

  • The parent project includes the usual Home and Account controllers (and associated views).
  • The Store project includes a Product controller (and its views).
  • The Accounts project maintains an Accounts controller (and its views).

Notice the “s” on the Accounts controller name – there is an Account controller and an Accounts controller in the application – we’ll come back to that.

Inside the parent project, use the AreaRegistration class to magically register all the routes in all child projects.

   <pre>public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    <strong>AreaRegistration.RegisterAllAreas();</strong>

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" }
    );
}</pre>

The AreaRegistration class will scan assemblies looking for types derived from AreaRegistration. It will instantiate these types and execute a method to  give the the child projects a change to register their own routes. For example, the Accounts sub-project registers it’s routes with the following code:


<pre>public class Routes : AreaRegistration
{
    public override string AreaName
    {
        get { return "Accounts"; }
    }

    public override void RegisterArea(
                           AreaRegistrationContext context)
    {
        context.MapRoute(
            "Accounts_Default",
            "Profile/{action}/{id}",
            new { controller = "Accounts",
                  action = "Index", id = "" }
        );
    }
}</pre>

With routes in place you can now generate links that reach specific areas. The following snippet creates a link to the Accounts controller in the Accounts sub-project…


<pre><%= Html.ActionLink("Accounts", "Index", "Accounts",
    new { area = "accounts" }, null)%></pre>

… while this one links to the Account controller in the parent project…


<pre><%= Html.ActionLink("Log On", "LogOn", "Account",
              new { area = "" }, null)%></pre>
Conflict!

What happens if two of the projects have a controller with the same name? For instance, if both the parent project and the Accounts project have an AccountController (with no “s” on Account).

If you try to reach the AccountController inside the Accounts area – everything should work. This is because the AreaRegistrationContext used to register routes for the Accounts area is automatically adding a namespace value to restrict the controller search. It’s like using the following code:


<pre>namespace Accounts
    //      ^^
    // the namespace of the area registration type
    // is used by default when mapping routes
{
    public class Routes : AreaRegistration
    {
        public override string AreaName
        {
            get { return "Accounts"; }
        }

        public override void
            RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Accounts_Default",
                "Profile/{action}/{id}",
                new { controller = "Account",
                      action = "Index", id = "" },
                null,
                new string[] { "Accounts" }
                // ^ this is explicitly specifying
                // the namespace as "Accounts",
                // but "Accounts" is the
                // default, so this isn't needed
            );
        }
    }
}</pre>

Note that the AccountController doesn’t have to live in the root Accounts namespace. It can live further down the hierarchy (like Accounts.Controllers.AccountController) and the factory will still find it.

The child area is in good shape, but a problem can occur if you try to reach the AccountController in the parent project. If the routes for the parent project were not given a any namespace values (which they aren’t by default), then  the default controller factory will become angry and throw an exception at runtime.

The controller name ‘Account’ is ambiguous between the following types:

MvcAreasMultiProject.Controllers.AccountController

Accounts.Controllers.AccountController

The easiest solution is to include the namespace(s) for your parent project controllers when registering routes in the parent area.


<pre>routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" },
    null,
    // namespaces ->
    new string[] { "MvcAreasMultiProject" }
);</pre>

Summary
  • You need to specify namespaces when registering routes if you have duplicate controller names.
  • The AreaRegistrationContext will automatically include a namespace value when registering routes in a child area. The namespace is the same namespace as the type used to register routes.