In der aktuellen Beta von ASP.NET MVC sieht das Standard-Projektlayout folgende Struktur vor:
- /Controllers/HomeController.cs
- /Views/Home/Index.aspx
- /Views/Home/About.aspx
Das ist absolut übersichtlich und ausreichend für kleinere Websites, aber schon dann, wenn man seine Seite in zwei logische Bereiche unterteilen will und sich einzelne Elemente wiederholen, sollte das Ganze weiter verschachtelt werden können. Beispiel:
Es gibt einen allgemein gültigen Bereich für Impressum usw. dazu noch ein Forum und einen Katalog. Alle drei Bereiche haben eine "Startseite". Im herkömmlichen Layout würde das Ganze dann so aussehen:
- /Controllers/CommonHomeController.cs
- /Controllers/ForumHomeController.cs
- /Controllers/CatalogHomeController.cs
Die entsprechenden URLs dazu:
- /CommonHome
- /ForumHome
- /CatalogHome
Nicht besonders schön, oder? Also sollte man es logisch aufteilen. Das Problem: im derzeitigen Stand von ASP.NET MVC ist das nicht vorgesehen, und da "Beta" bei Microsoft auch immer weitestgehend "Feature Complete" bedeutet, dürfte das Ganze auch trotz neuerlicher Bemühungen nicht mehr den Weg in die finale Version finden.
Trotzdem gibt es einen Weg, das auch heute schon selbst "hinzutricksen", wobei da gar nicht viel Aufwand notwendig wird. Das Layout sollte also wie folgt aussehen:
- /App/Controllers/Common/HomeController.cs
- /App/Controllers/Forum/HomeController.cs
- /App/Controllers/Catalog/HomeController.cs
Die URLs dazu:
Nötig sind dafür eigentlich nur zwei kleinere Eingriffe:
1. Die Area muss in der URL mitgegeben werden
Hierfür ergänzen wir erstmal nur die Default-Route um den Parameter.
1: routes.MapRoute(
2: "Default",
3: "{area}/{controller}/{action}/{id}",
4: new { area = "Common", controller = "Home", action = "Index", id = "" }
5: );
2. Der Zugriff auf den richtigen Controller in der richtigen Area muss klappen
Das erreicht man durch eine eigene ControllerFactory:
1: public class AreaControllerFactory : DefaultControllerFactory
2: {
3: protected override Type GetControllerType(string controllerName)
4: {
5: object areaName;
6: if (RequestContext != null && RequestContext.RouteData.Values.
7: TryGetValue("area", out areaName))
8: {
9: string ns = string.Empty;
10: switch (areaName.ToString().Trim().ToLower())
11: {
12: case "common":
13: ns = "MvcAreas.App.Controllers.Common";
14: break;
15: case "catalog":
16: ns = "MvcAreas.App.Controllers.Catalog";
17: break;
18: case "forum":
19: ns = "MvcAreas.App.Controllers.Forum";
20: break;
21: }
22: if (ns.Length > 0)
23: return Type.GetType(ns + "." + controllerName + "Controller");
24: }
25: return base.GetControllerType(controllerName);
26: }
27: }
3. Die richtige View in der richtigen Area muss gefunden werden
Auch nicht besonders schwierig, da alle nötigen Informationen ja inzwischen bereitstehen. Nötig hierfür ist eine eigene ViewEngine.
1: public class AreaViewEngine : WebFormViewEngine
2: {
3: public override ViewEngineResult FindView(ControllerContext
4: controllerContext, string viewName, string masterName)
5: {
6: string areaName = controllerContext.RouteData.
7: GetRequiredString("area");
8: string controllerName = controllerContext.RouteData.
9: GetRequiredString("controller");
10: string path = string.Format("~/App/Views/{0}/{1}/{2}.aspx",
11: areaName, controllerName, viewName);
12: return new ViewEngineResult(CreateView(controllerContext,
13: path, null), this);
14: }
15: }
4. Registrierung von ControllerFactory und ViewEngine
1: protected void Application_Start()
2: {
3: RegisterRoutes(RouteTable.Routes);
4: ControllerBuilder.Current.
5: SetControllerFactory(new AreaControllerFactory());
6: ViewEngines.Engines.Add(new AreaViewEngine());
7: }
That's it. Soweit ich das nun auf die Schnelle beurteilen konnte, funktioniert innerhalb einer Area alles, also auch die Verwendung der Html-Helper wie ActionLink usw. - was natürlich nicht funktioniert ist das "Area-übergreifende" Setzen von dynamischen Links - wie auch, ASP.NET MVC weiß von den Areas ja erst oder nur beim routen der Anfragen, nicht aber beim Erstellen der Links und Rendern der Seiten. Hier muss man sich einstweilen mit festverdrahteten Links begnügen, was meistens auch ausreichen sein sollte. Irgendwann wird uns dann hoffentlich Microsoft erlösen.
Nachfolgend findet sich noch eine Beispielanwendung.
Downloads