Areas

By Dhananjay Kumar and Rick Anderson

Areas are an ASP.NET MVC feature used to organize related functionality into a group as a separate namespace (for routing) and folder structure (for views). Using areas creates a hierarchy for the purpose of routing by adding another route parameter, area, to controller and action.

Areas provide a way to partition a large ASP.NET Core MVC Web app into smaller functional groupings. An area is effectively an MVC structure inside an application. In an MVC project, logical components like Model, Controller, and View are kept in different folders, and MVC uses naming conventions to create the relationship between these components. For a large app, it may be advantageous to partition the app into separate high level areas of functionality. For instance, an e-commerce app with multiple business units, such as checkout, billing, and search etc. Each of these units have their own logical component views, controllers, and models. In this scenario, you can use Areas to physically partition the business components in the same project.

An area can be defined as smaller functional units in an ASP.NET Core MVC project with its own set of controllers, views, and models.

Consider using Areas in an MVC project when:

  • Your application is made of multiple high-level functional components that should be logically separated

  • You want to partition your MVC project so that each functional area can be worked on independently

Area features:

  • An ASP.NET Core MVC app can have any number of areas

  • Each area has its own controllers, models, and views

  • Allows you to organize large MVC projects into multiple high-level components that can be worked on independently

  • Supports multiple controllers with the same name - as long as they have different areas

Let's take a look at an example to illustrate how Areas are created and used. Let's say you have a store app that has two distinct groupings of controllers and views: Products and Services. A typical folder structure for that using MVC areas looks like below:

  • Project name

    • Areas

      • Products

        • Controllers

          • HomeController.cs

          • ManageController.cs

        • Views

          • Home

            • Index.cshtml
          • Manage

            • Index.cshtml
      • Services

        • Controllers

          • HomeController.cs
        • Views

          • Home

            • Index.cshtml

When MVC tries to render a view in an Area, by default, it tries to look in the following locations:

/Areas/<Area-Name>/Views/<Controller-Name>/<Action-Name>.cshtml
   /Areas/<Area-Name>/Views/Shared/<Action-Name>.cshtml
   /Views/Shared/<Action-Name>.cshtml

These are the default locations which can be changed via the AreaViewLocationFormats on the Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions.

For example, in the below code instead of having the folder name as 'Areas', it has been changed to 'Categories'.

services.Configure<RazorViewEngineOptions>(options =>
   {
       options.AreaViewLocationFormats.Clear();
       options.AreaViewLocationFormats.Add("/Categories/{2}/Views/{1}/{0}.cshtml");
       options.AreaViewLocationFormats.Add("/Categories/{2}/Views/Shared/{0}.cshtml");
       options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
   });

One thing to note is that the structure of the Views folder is the only one which is considered important here and the content of the rest of the folders like Controllers and Models does not matter. For example, you need not have a Controllers and Models folder at all. This works because the content of Controllers and Models is just code which gets compiled into a .dll where as the content of the Views is not until a request to that view has been made.

Once you've defined the folder hierarchy, you need to tell MVC that each controller is associated with an area. You do that by decorating the controller name with the [Area] attribute.

...
   namespace MyStore.Areas.Products.Controllers
   {
       [Area("Products")]
       public class HomeController : Controller
       {
           // GET: /Products/Home/Index
           public IActionResult Index()
           {
               return View();
           }

           // GET: /Products/Home/Create
           public IActionResult Create()
           {
               return View();
           }
       }
   }

Set up a route definition that works with your newly created areas. The 🔧 Routing to Controller Actions article goes into detail about how to create route definitions, including using conventional routes versus attribute routes. In this example, we'll use a conventional route. To do so, open the Startup.cs file and modify it by adding the highlighted route definition below.

...
   app.UseMvc(routes =>
   {
     routes.MapRoute(name: "areaRoute",
       template: "{area:exists}/{controller=Home}/{action=Index}");

     routes.MapRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}");
   });

Browsing to http://<yourApp>/products, the Index action method of the HomeController in the Products area will be invoked.

  • Generating links from an action within an area based controller to another action within the same controller.

    Let's say the current request's path is like /Products/Home/Create

    HtmlHelper syntax: @Html.ActionLink("Go to Product's Home Page", "Index")

    TagHelper syntax: <a asp-action="Index">Go to Product's Home Page</a>

    Note that we need not supply the 'area' and 'controller' values here as they are already available in the context of the current request. These kind of values are called ambient values.

  • Generating links from an action within an area based controller to another action on a different controller

    Let's say the current request's path is like /Products/Home/Create

    HtmlHelper syntax: @Html.ActionLink("Go to Manage Products’ Home Page", "Index", "Manage")

    TagHelper syntax: <a asp-controller="Manage" asp-action="Index">Go to Manage Products’ Home Page</a>

    Note that here the ambient value of an 'area' is used but the 'controller' value is specified explicitly above.

  • Generating links from an action within an area based controller to another action on a different controller and a different area.

    Let's say the current request's path is like /Products/Home/Create

    HtmlHelper syntax: @Html.ActionLink("Go to Services’ Home Page", "Index", "Home", new { area = "Services" })

    TagHelper syntax: <a asp-area="Services" asp-controller="Home" asp-action="Index">Go to Services’ Home Page</a>

    Note that here no ambient values are used.

  • Generating links from an action within an area based controller to another action on a different controller and not in an area.

    HtmlHelper syntax: @Html.ActionLink("Go to Manage Products’ Home Page", "Index", "Home", new { area = "" })

    TagHelper syntax: <a asp-area="" asp-controller="Manage" asp-action="Index">Go to Manage Products’ Home Page</a>

    Since we want to generate links to a non-area based controller action, we empty the ambient value for 'area' here.

Publishing Areas

To publish all views of the areas folder, in the project.json file include an entry in the publishOptions's include node like below:

"publishOptions": {
     "include": [
       "Areas/**/*.cshtml",
       ....
       ....
     ]