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”:
Zaglądając do Solution Explorer, przekonamy się, że nowy folder został dodany:
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:
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.
Świetny artykuł. Oszczędził mi sporo czasu.
Super artykul!
Potwierdzam! Dzięki za wpis