Explorando ASP.NET MVC 2.0… áreas

 ·  ☕ 7 min  ·  ✍️ eiximenis

    Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí

    No hace mucho Jorge Dieguez comentaba la salida de ASP.NET MVC 2.0 Preview 1. He estado investigando un poco las novedades del framework, y hoy quiero hablaros de lo que se conoce como áreas.

    Cuando hablamos de áreas no nos referimos a zonas de la pantalla (tipo webparts) sinó que las áreas en ASP.NET MVC permiten construir una aplicación en base a módulos. Actualmente en la preview 1, el sistema consiste en:

    • Definir la área principal: Un proyecto ASP.NET MVC completo, con sus vistas, sus controladores, sus hojas de estilo, …
    • Definir el resto de áreas (módulos). Cada área es un proyecto ASP.NET MVC que añade sus propias vistas y controladores. Cada área confía en el área principal para el resto de temas (p.ej. scripts y css están en el área principal). De igual modo toda la inicialización (global.asax) se realiza únicamente en el área principal: el resto de áreas no tienen global.asax.

    De esta manera se pretende poder gestionar mejor grandes aplicaciones: al estar formadas por áreas independientes, que a su vez son proyectos independientes de ASP.NET MVC, equipos distintos podrían realizar cada una de las áreas.

    En el archivo global.asax, del área principal, cuando registramos las rutas, mapeamos determinadas URLs a cada uno de las áreas. Imaginad que tenemos una área que contiene toda la parte de administración del sitio, podríamos añadir a global.asax, una línea tal como:

    routes.MapAreaRoute("Admin", "Admin_Default",
        "admin/{controller}/{action}/{id}",
        new string[] { " Admin.Controllers" });

    El primer parámetro es el nombre del área (luego veremos que el nombre tiene implicaciones), el segundo el nombre de la ruta (cada área puede tener tantas rutas como se desee), el tercer parámetro es la URL a enrutar y el cuarto parámetro es un array con todos los namespaces donde buscar los controladores.

    De esta manera todos las URLs que empiecen por /Admin/ serán enrutadas a la área Admin. P.ej. la URL /Admin/Users/View/20 sería enrutada al controlador “UsersController”, llamando a su acción View con el Id de 20. El último parámetro (un array de strings) es importante puesto que contiene todos los namespaces de los posibles controladores de la área de administración. Dado que son proyectos distintos pueden tener controladores con el mismo nombre, así que el motor de ASP.NET MVC debe saber el namespace a utilizar… En fin, que simplemente le estamos diciendo al motor de ASP.NET MVC que use el controlador que sea que esté en el namespace Admin.Controllers.

    P.ej. en mi caso estoy trabajando con 3 áreas (la principal y dos más) y tengo definida la siguiente tabla de enrutamiento:

    routes.MapAreaRoute("Game", "Game_Default", 
    "game/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Game.Controllers" }); routes.MapAreaRoute(
    "Admin", "Admin_Default", "admin/{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Admin.Controllers" }); routes.MapAreaRoute("Main",
    "Main_Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" }, new string[] { "Web.Controllers" });

    Todas las URLs que empiecen por /Game/ se enrutarán al área que implementa la aplicación web en sí (efectivamente es un juego :p), mientras que todas las URLs que empiecen por /Admin/ se enrutarán al área que implementa la zona de administración. Finalmente el resto de URLs (que no empiecen ni por /Game/ ni por /Admin/ serán enrutadas al área principal. Fijaos que las 3 áreas definen un controlador “Home” pero no es problema, puesto que el namespace es distinto 🙂

    Obviamente se puede generar un enlace desde una área a otra (sino maldita la gracia). Así de esta manera se genera un enlace a la acción About del controlador Home de la área actual:

    <%= Html.ActionLink("Pulse aquí", "About", "Home") %>

    En fin, como siempre… Para cambiar de área simplemente debemos pasar el nombre del área (debe usarse el mismo nombre con el cual se ha registrado el área en MapAreaRoute):

    <%= Html.ActionLink("Pulse aquí", "About", "Home", 
    new { area = "Game" } , null)%>

    Esto genera un enlace a la acción About del controlador Home del área Game (en mi caso una URL tipo /Game/Home/About).

    Todo muy bonito pero…

    … estamos en una preview 1, así que no todo es tan directo.

    Imaginad el caso del enlace que he puesto que genera una URL a la acción About del controlador Home del área Game. Imaginad que el código de la acción About de dicho controlador es:

    public ActionResult About() { return View(); }

    Esto debería retornar la vista que està en Views/Home/About.aspx del proyecto que es el área Game… Pues no 🙁 Esto utiliza la vista que está en Views/Home/About.aspx del proyecto que es el área principal. Si el área principal no tiene esta vista todo rebienta. Obviamente este comportamiento ni es correcto (cada área debe proporcionar sus propias vistas) ni útil ya que si de todos modos el proyecto del área principal debe contener todas las áreas… donde está la capacidad de poder modularizar el desarrollo?

    Bueno, todo esto viene debido a que estamos en preview 1, y simplemente debemos cambiar la configuración de los proyectos. Si editamos los ficheros .csproj de cada aplicación web, veremos primero unas líneas como estas (que como nos indican debemos descomentar):

    <!-- To enable MVC area subproject support, uncomment the following two lines:
    <UsingTask TaskName="Microsoft.Web.Mvc.Build.CreateAreaManifest" 
    AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=31bf3856ad364e35" /> <UsingTask TaskName="Microsoft.Web.Mvc.Build.CopyAreaManifests"
    AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral,
    PublicKeyToken=31bf3856ad364e35" />
    -->

    y luego otras como las siguientes (que debemos descomentar según el proyecto sea el área principal o no):

    <!-- If this is an area child project, uncomment the following line:
    <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" 
    AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
    ContentFiles="@(Content)" />
    --> <!-- If this is an area parent project, uncomment the following lines: <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent"
    AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
    ContentFiles="@(Content)" />
    <CopyAreaManifests ManifestPath="$(AreasManifestDir)"
    CrossCopy="false" RenameViews="true" />
    -->

    Así pues ya vemos lo que se debe hacer, no? 😉 Descomentamos en cada proyecto las líneas que pertoquen y listos!

    Esto básicamente lo que hace es establecer una custom action build que copia las vistas de las áreas hijas dentro de la carpeta Views del área principal. De esta manera dentro de la carpeta Views del área principal tendremos:

    Views/Home/About.aspx –> Vista para la acción Home.About del área principal

    Views/Areas/Game/Home/About.aspx –> Vista para la acción Home.About del área “Game” (copia de la vista Views/Home/About.aspx) del proyecto que es el área Game.

    Nota Importante: Para que todo esto funcione el nombre del área debe corresponderse con el nombre del ensamblado del proyecto que genera dicha área. En mi caso, el proyecto del área “Game” debe generar el ensamblado Game.dll. Si el nombre del área y el nombre del ensamblado del proyecto es distinto, no funcionará porque…

    … el motor de ASP.NET MVC usa el nombre del área (definido en la llamada a MapAreaRoute)

    …y en cambio la vista estará copiada en Views/Areas/

    Si no quereis que el assembly se llame igual que nombre del área podeis modificar la línea:

    <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" 
    AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)"
    ContentFiles="@(Content)" />

    que hemos descomentado antes y poner en AreaName el nombre lógico del área (el que habéis usado en RegisterMapRoute). De todos modos aunque funciona no os lo recomiendo ya que entonces las vistas de este ensamblado se duplican dentro de Views/Areas… es decir se siguen copiando como Views/Areas/ y además como View/Areas/… o sea que lo más práctico es que los assemblies se llamen como las áreas y listos 🙂

    Toda esta paranoia de editar el fichero de proyecto desaparecerá en futuras versiones donde tendremos un template de proyecto para aplicación ASP.NET MVC “área principal” y otro para “área hija”… recordad: preview 1 😉

    En fin… espero que esto os haya servido para ver un poco que son las áreas en ASP.NET MVC 2.0 Preview 1. Resumiendo:

    • Una área es un proyecto de ASP.NET MVC 2.0 que implementa parte de una aplicación entera (p.ej. el módulo de administración)
    • Una aplicación ASP.NET MVC 2.0 se compone de una (y sólo una) área padre y varias áreas hijas (una por módulo)
    • El área padre proporciona los estilos (css) y ficheros de javascript, así como la inicialización/finalización de la aplicación (global.asax). Puede también proporcionar vistas y controladores
    • Las áreas hijas proporcionan vistas y controladores adicionales
    • El método MapAreaRoute sirve para enrutar determinadas URLs a los controladores de una área determinada
    • Todo está bastante verde todavía pero bueno… 🙂

    Saludos!!!

    Si quieres, puedes invitarme a un café xD

    eiximenis
    ESCRITO POR
    eiximenis
    Compulsive Developer