ASP.NET MVC Areas

Im większy projekt tym więcej kontrolerów i widoków w solucji. Istnieje wiele sposobów na poddział projektu na kilka części. Nie zawsze jednak jest sens tworzenia nowych bibliotek i zwykle lepiej zastosować po prostu podział za pomocą przestrzeni nazw. “Areas” to po prostu wydzielenie kilku kontrolerów i widoków do osobnej przestrzeni nazw. Domyślnie wszystkie kontrolery znajdują się w folderze Controllers a widoki w Views. Przy dużych projektach jest to nieczytelne i trudne w utrzymaniu. Warto wtedy rozdzielić to na kilka obszarów – wystarczy dodać (z menu kontekstowego) “Area”:

image

Zaglądając do Solution Explorer, przekonamy się, że nowy folder został dodany:

image

Oprócz folderów Controllers i Views, mamy Areas, który zawiera właśnie dodany obszar o nazwie Admin. W Admin znajdziemy z kolei Controllers i Views. Innymi słowy, możemy tworzyć nowe obszary z własnymi kontrolerami i widokami. Zamiast wszystko umieszczać w głównych folderach, warto rozdzielić to na logiczne części (takie “podprojekty”).

Oprócz folderów, została dodana również klasa AdminAreaRegistration.cs, która znajduje się w Areas\Admin:

public class AdminAreaRegistration : AreaRegistration
{
   public override string AreaName
   {
       get
       {
           return "Admin";
       }
   }

   public override void RegisterArea(AreaRegistrationContext context)
   {
       context.MapRoute(
           "Admin_default",
           "Admin/{controller}/{action}/{id}",
           new { action = "Index", id = UrlParameter.Optional }
       );
   }
}

Klasa zawiera konfigurację danego obszaru (area). Najważniejszą częścią jest chyba zdefiniowany routing. Jak widać z metody RegisterArea, wszystkie kontrolery w danym obszarze można wywołać za pomocą URL w postaci “Admin/{controller}/{action}. Jeśli stworzyliśmy np. kontroler UserController to akcja Index będzie osiągalna za pomocą: http://localhost:54256/Admin/User/Index.

Z kolei w pliku global.asax.cs znajduje się rejestracja obszaru:

public class MvcApplication : System.Web.HttpApplication
{
   protected void Application_Start()
   {
       AreaRegistration.RegisterAllAreas();

       WebApiConfig.Register(GlobalConfiguration.Configuration);
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       RouteConfig.RegisterRoutes(RouteTable.Routes);
       BundleConfig.RegisterBundles(BundleTable.Bundles);
       AuthConfig.RegisterAuth();
   }
}

Spróbujmy dodać kolejny kontroler o nazwie Home. Powstała struktura powinna wyglądać następująco:

image

Mamy zatem dwa kontrolery o nazwie HomeController. Jeden znajduje się w Admin/Home/Controllers a drugi w głównym Controllers. Wywołując http://localhost:54256/Admin/Home/Index sprawa jest prosta i zostanie wykonany prawidłowy kontroler. Co z kolei jednak gdy chcemy wykonać główny kontroler? Próbując wykonać /Home/Index dostaniemy wyjątek:

Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.

The request for 'Home' has found the following matching controllers:
MvcApplication3.Areas.Admin.Controllers.HomeController
MvcApplication3.Controllers.HomeController 

Zaglądając do pliku App_Start/RouteConfig.cs przekonamy się, że zdefiniowany routing jest zbyt ogólny:

routes.MapRoute(
           name: "Default",
           url: "{controller}/{action}/{id}",
           defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
       );

Domyślnie wszystkie przestrzenie nazw są przeszukiwane. Z tego względu /Home/Index zostanie dopasowany do kilku kontrolerów.  Rozwiązaniem jest jawne zdefiniowanie namesapce:

 routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, 
                namespaces:new[] { "MvcApplication3.Controllers" }
            );

Teraz główny routing najpierw przeszuka klasy znajdujące się w głównym folderze Controllers. W poprzednim przykładzie wszystkie przestrzenie nazw były brane pod uwagę i jeśli znaleziono kilka dopasowań to wyjątek był wyrzucany.

Generowanie linków do konkretnej akcji odbywa się tak samo za pomocą @Html.ActionLink. W większości sytuacji nie trzeba przekazywać dodatkowych parametrów – dlatego nigdy nie polecam używania bezpośrednio <a href…/>.

Z kolei jeśli chcemy z obszaru A, stworzyć link do obszaru B, wtedy musimy jakoś to oznaczyć. Domyślnie wszystkie linki generowane są dla tego samego obszaru, w  którym znajduję się dany widok. Na szczęście wystarczy przekazać po prostu dodatkową zmienną @area:

@Html.ActionLink("DisplayText", "Index", new { area = "CustomArea" })

Powyższa metoda wygeneruje link do /CustomArea/Home/Index.

Inny scenariusz to link z jakiegoś obszaru do akcji w głównym folderze:

@Html.ActionLink("Jakis tekst", "Index", new { area = "" })

Przekazując pustą nazwę, zostanie wygenerowany odnośnik do głównego kontrolera tzn. /Home/Index.

3 thoughts on “ASP.NET MVC Areas”

Leave a Reply

Your email address will not be published.