Está en la página 1de 50

ASP.

NET MVC Framework (Primera parte)


Hace un par de semanas hablamos sobre el nuevo MVC Framework (Modelo Vista Controlador) que vamos a soportar como una caracterstica opcional. Nos aporta un modelo estructurado que se preocupa de separar claramente las diferentes partes de una aplicacin, y hace mucho ms sencillo la realizacion de test unitarios y soporta un workflow TDD. Tambin nos ayuda a tener un mayor control sobre las URLs que publicamos en nuestras aplicaciones, y opcionalmente mayor control sobre el HTML que se emite para ellas. Desde entonces he estado respondiendo un montn de preguntas de gente con ganas de aprender sobre el. Debido al nivel de interes creo que poda tener sentido juntar unos cuantos posts que describan cmo usarlo en ms detalle. ste es el primero de una serie de post que escribir en las proximas semanas. Una aplicacin de E-commerce simple Vamos a usar una aplicacin de e-comerce simple para ilustrar cmo funciona el ASP.NET MVC Framework. Para el post de hoy, implementaremos el listado/navegacin de productos de la tienda. Vamos a crear una tienda que permita a los usuarios navegar por una lista de categoras de productos cuando van a la URL /products/categories:

Cuando un usuario hace clic en una categora de la pgina, llegarn a la lista de productos de esa categoras con una url con este formato: Products/list/Nombrecategora -:

Cuando un usuario hace clic en un producto, llegar a la URL del detalle del producto -/Products/Detail/ProductID - que muestra los detalles del producto seleccionado:

Crearemos toda esta funcionalidad con el ASP.NET MVC Framework. Con esto nos aseguraremos mantener una separacin de conceptos entre los diferentes componentes de la aplicacin, y nos permitir integrar test unitarios y un desarrollo guiado por test. Crear una aplicacin ASP.NET MVC El ASP.NET MVC Framework incluye una serie de Templates para Visual Studio que hacen fcil crear una aplicacin. Slo con ir al men File->New Project elegimos "ASP.NET MVC Web Application" tendremos el esqueleto para una aplicacin web. Por defecto, cuando creamos una nueva aplicacin de esta forma, Visual Studio crear una nueva solucin y aadir dos proyectos. El primero es un proyecto web en el que implementaremos nuestra aplicacin. El segudno es un proyecto de testing que usaremos para escribir test unitarios:

Podemos usar cualquier framework de test unitarios (NUnit, MBUnit, MSTest, XUnit, y otros). VS 2008 Professional incluye un proyecto de testing para MSTest (VS 2005 necesitva el Visual Studio Team System SKU). Tambin estamos trabajando en templates para NUnit, MBUnit y otros frameworks de test unitarios, de forma que podis usarlos para crear vuestra aplicacin y tener un proyecto de test inmediato para usarlo. Comprendiendo la estructura de directorios del proyecto La estructura por defecto de una aplicacin ASP.NET MVC tiene 3 directorios:

/Controllers /Models /Views

Como podris suponer, os aconsejamos que pongais las clases de controladores en el directorio /Controllers, las de modelo de datos en el directorio /Models, y las vistas en el directorio /Views. ASP.NET MVC Framework no te obliga a usar esta estructura, el template usa este patrn y os lo recomendamos ya que es una forma fcil de estructurar la aplicacin. A menos que tengis una buena razn para usar una estructura diferente, os recomiendo que useis este. Mapeando URLs a clases de controladores. En la mayora de frameworks para web (ASP, PHP, JSP, ASP.NET WebForms, etc), las Urls suelen corresponderse con archivos en el disco. Por ejemplo, "/Products.asp" o "/Products.php" suelen tener un archivo Products.aspx o Products.php en el disco que administra el proceso. Cuando una peticin http para ana aplicacin web llega al servidor web, el web framework ejecuta el cdigo de dicho archivo del disco, y este cdigo asume la administracin de la peticin. Normalmente ese cdigo usa HTML en Products.aspx o Products.php para generar una respuesta adecuada al cliente. Los frameworks MVC mapean esas urls de una forma algo diferente. En lugar de mapear las urls a archivos del disco, mapean las urls a clases directamente. Estas clases se llamana "Controladores" y procesa todas las peticiones, administrar la interaccin del usuario, y ejecutan la lgica necesaria basndose en ellas. Una clase Controlador llamar a una "vista" que genera el HTML de respuesta:

El ASP.NET MVC Framework tiene un motor de mapeados de urls muy potente que aporta una gran flexibilidad en cmo mapea a las clases Controlador. Podemos usarlo para crear reglas de rutado que usar ASP.NET para evaluar las URLs entrantes y seleccionar un Controlador que la ejecute. Tambin podemos hacer que el motor de rutado parsee las variables que definamos en las URLs y que ASP.NET las pase a nuestro Controlador como argumentos. Veremos escenarios ms avanzados en los que se usa el motor de rutado de urls en otro post de esta serie. Rutado por defecto de URL de ASP.NET MVC a las clases Controlador. Por defecto los proyectos de ASP.NET MVC estn tienen unas reglas de rutado de urls preconfiguradas que nos permiten empezar a crear aplicaciones sin tener que configurar explicitamente nada. De esta forma podemos empezar a escribir el cdigo usando un conjunto de convenciones para nombrar las urls que van a usarse en nuestra aplicacin en ASP.NET en el archivo Global.asax que se crea. El convenio por defecto es mapear el path de la url de una peticin HTTP (por ejemplo: /Products/) a una clase cuyo nombre siga el patrn UrlPathController (por ejemplo: una url que termine con /Products/ se mapear a la clase ProductsController). Para crear la funcionalidad de navegacin de productos en nuestro ejemplo, aadimos la clase "ProductsController" a nuestro proyecto (podemos usar el men "Add New Item" de Visual Studio para crear una clase Controlador desde un template):

Nuestra clase ProductsController heredar de System.Web.MVC.Controller. Aunque no es necesario esta herencia - pero esta clase base contiene algunos mtodos auxiliares y funcionalidad que podra hacernos falta para aprovecharla ms tarde:

Una vez que definimos esta clase, el framework ASP.NET MVC la usar para procesar todas las peticiones cuya url contenga la cadena "/Products". Es decir, ser llamada para procesar las urls "/Products/Categories", "/Products/List/Beverages", y "/Products/Detail/3". En otro post veremos cmo aadir un ShoppingCartController (para permitir que los usuarios administren sus carritos d compra) y un AccountController (para administrar sus cuentas de usuarios). Una vez que las definamos, las urls que contgan "/SoppingCart/" y "/Account/" seran procesadas por aquellas. Nota: ASP.NET MVC no requiere que se use este convenio de nombres. La razn por la que nuestra aplicacin usa este convenio por defecto es porque hay una regla de rutado que configura esta opcin que se aade automticamenet a las clases de nuestras aplicaciones ASP.NET cuando creamos un proyecto ASP.NET MVC con Visual Studio. Si no os gusta esta regla, o queris personalizarla, slo tenis que iros a la clase de la aplicacin (en global.asax) y cambiarlo. Veremos cmo hacer esto en otro post (cuando veamos algunos escenarios interesantes que nos permite el rutado de URLs). Comprendiendo los mtodos de accin de los controladores. Ahora que tenemos la clase ProductsController podemos empezar a aadir lgica para procesar las urls con el path "/Products". Cuando definimos los casos de uso de nuestra tienda al principio del blog, dijimos que ibamos a implementar tres escenarios: 1) Navegar por las categoras de Productos, 2) Listar los productos de una categora, y 3) Mostrar los detalles de un producto. Vamos a usar las siguientes urls para estos escenarios: URL Format /Products/Categories /Products/List/Category /Products/Detail/ProductID Behavior Browse all Product Categories List Products within a Category Show Details about a Specific Product URL Example /Products/Categories /Products/List/Beverages /Products/Detail/34

Hay un par de formas de escribir el cdigo en nuestra clase ProductsController para procesar estos tres tipos de urls. Una forma podra ser "Ejecutar" un mtodo en la clase base Controller y escribir nuestra lgica if/else/switch para ver qu url se ha solicitado, y entonces ejecutar la logica necesaria en cada caso. Una forma ms sencilla es usar una caracterstica del framework MVC que nos permite definir "mtodos de accin" en nuestro controlador, y que la clase base Controller invoque el mtodo apropiado. Por ejemplo, podemos aadir los siguientes mtodos de accin a nuestra clase ProductsController :

Las reglas de rutado de urls que estn configuradas por defecto cuando creamos un nuevo proyecto tratan los sub-path de la url que siguen al nombre del controlador como el nombre de la accin de la peticin. As que cuando recibimos una peticin de la forma "/Products/Categories", la regla tratar la parte "Categories" como el nombre de la accin, y se invocar el mtodo Categories() para procesar la peticin. Si recivimos una peticin a la url /Products/Detail/5, la regla cojer el nombre "Detail" como el nombre de la accin y se invocar al mtodo Detail(), etc. Nota: ASP.NET MVC no obliga a usar este convenio de nombres. Si queremos usar otro patrn de mapeado para las urls, podemos cambiarlo en la clase de la aplicacin de ASP.NET (en Global.asax) y cambiarlo. Mapeando parmetros URL a mtodos de accin Hay varias formas de acceder a los parmetros de las urls en los mtodos de accin de las clases controlador. La clase base Controller expone un conjunto de objetos Request y Response que podemos usar. EStos objetos tienen la misma estructura que los objetos HttpRequest/HttpResponse a los que estamos acostumbrados en ASP.NET. La diferencia es que estos objetos estn basados en interfaces en lugar de en clases selladas (sealed) (MVC trae las interfaces System.Web.IHttpRequest y System.Web.IHttpResponse). Lo bueno de esto es que ahora podemos implementarlos como queramos - lo que nos permite crear test unitarios para clases controlador. Lo veremos en ms profundidad en futuros post. Aqu tenis un ejemplo de cmo usar el API Request para obtener un ID querystring desde la accin Detail de nuestra clase ProductsController:

ASP.NET MVC tambin nos permite mapear automticamente parmetros de la url como parmetros de mtodos de accin. Por defecto, si tenemos un parmetro en algn mtodo de accin, MVC mirar los datos de la peticin para ver si hay algn valor que se corresponda con el mismo nombre. Si lo hay, lo pasar automticamente como parmetro del mtodo de accin. Por ejemplo, podemos reescribir el mtodo de accin Detail use esta caracterstica:

Adems de mapear los valores de los argumentos de la querystring de una peticin, ASP.NET MVC tamibn nos permite usar la infraestructura de mapeo de rutas para incrustar valores en la misma url (por ejemplo: en ludgar de /Products/Detail?id=3 podramos usar /Products/Detail/3). La regla de mapeo por defecto cuando creamos un nuevo proyecto MVC es: "[controller]/[action]/[id]". Vemos que si hay algn sub-path en la direccin despus del nombre del mtodo de accin, lo tratar como un parmetro - y podremos pasarlo directamente al mtodo de accin como argumento:

Podemos hacer lo mismo para la accin List (Listar) de manera que le pasamos el nombre de la categora en la url (por ejemplo: /Products/List/Beverages). Para hacer el cdigo ms claro, he hecho un truco en las reglas de rutado en lugar de pasar un argumento con nombre "id" sea "category" para esta accin. Aqu teneis una versin de nuestra clase ProductsController que ahora tiene un rutado url completo y un soporte para el mapeo de parmetros:

Fijos cmo en la accin List coge el parmetro de la categora como parte de la URL, y un ndice de pgina opcional (implementaremos paginado en el servidor y usaremos ese valor para indicar qu pgina de categora mostrar en la peticin). Los parmetros opcionales se manejan usando tipos de datos nullables en los mtodos de accin de los controladores. Debido a que el parmetro de nuestra accin List es nullable (eso es lo que significa "int?"), el framework MVC pasar el valor si est en la url - o pasar un nulo si no. Leed este post para aprender cmo funcionan los tipos nulables. Construllendo objetos de nuestro modelo de datos. Ya tenemos la clase ProductsController y tres mtodos de accin listas para procesar peticiones web. El siguiente paso es crear algunas clases que nos ayuden a trabajar con nuestra base de datos para obtener los datos necesarios para manejar esas peticiones web. En un mundo MVC los "modelos" son componentes de una aplicacin responsables de mantener el estado. En las aplicaciones web, este estado se guarda en una base de datos (por ejemplo: tendremos un objeto producto que se usa para representar los datos de un producto de la tabla Products en nuestra base de datos SQL). El framework ASP.NET MVC nos permite usar cualquier patrn de acceso a datos o framework que queramos para obtener y trabajar con los modelos. Si queremos usar ADO.NET DataSets/DataReaders (o abstracciones encima de ellos) podemos. Si preferimos usar un ORM (Object Relational Mapper) como NHibernate, LLBLGen, WilsonORMapper, LINQ to SQL/LINQ to Entities podemos tambin. Para nuestra aplicacin de ejemplo usaremos el ORM que viene con .NET 3.5 y VS 2008 llamado LINQ to SQL. Podis aprender ms sobre l en la serie de post que escrib (en concreto aqu tenis los post: post1, post2, post3 y post4). Empezaremos haciendo clic con el botn derecho en el directorio "Models" de nuestro proyecto MVC y elegimos "Add New Item" para aadir un modelo LINQ to SQL. En el diseador de LINQ to SQL defeiniremos tres clases para mapear las tablas Categories, Products, y Suppliers de la base de datos de ejemplo de Northwind:

Una vez definido nuestro modelo LINQ to SQL, aadiremos una nueva clase parcial NorthwindDataContext al directorio Models:

En esta clase definiremos unos cuantos mtodos que nos servirn para encapsular algunas expresiones LINQ que usaremos para obtener los objetos Category de nuestra base de datos, obtener todos los productos de una categora y un producto concreto a partir del campo ProductID:

Estos mtodos nos harn ms facil y claro obtener los objetos del modelo de datos que necesitamos para nuestra clase ProductsController (sin tener que escribir ninguna expresin LINQ en nuestra clase controlador):

Ahora tenemos todo el cdigo necesario para la funcionalidad de la clase ProductsController. Terminando la implementacin de la clase ProductsController Los controladores de una aplicacin basada en MVC son los responsables de procesar las peticiones entrantes, administrar las entradas de usuario y sus interacciones, y ejecutar la lgica necesaria (obtener y actualizar el modelo de datos, etc...). Normalmente los controladores NO generan la respuesta HTML. Esta tarea es cosa de los componentes "View" (o vistas) de la aplicacin - que se implementan en clases a parte de los controladores. Las vistas se centrar en encapsular toda la lgica de presentacin y no deben contener nada de la lgica de la aplicacin o accesos a la base de datos (esto es tarea de los controladores). En un workflow web MVC tpico, los mtodos de accin de los controladores administran las peticiones web, usan los parmetros para ejecutar la lgica apropiada, obtienen o actualizan los modelos de datos de la base de datos, y entonces eligen una Vista para renderizar la respuesta al navegador cliente. Como parte de elegir la vista adecuada para renderizar, el controlador le pasar explicitamente (como argumento) todos los datos y variables necesarios para que la vista genere la respuesta adecuada:

Os estaris preguntando cuales son los benefiicos de separar las vistas y los controladores de esta manera? Porqu no ponerlos todos en la misma clase? La primera motivacin de particionar la aplicacin de esta manera es para ayudar a la separacin entre lgica de negocio y la generacin de la interfaz de suuario. Esto hace mucho ms sencillo el hacer test unitarios a nuestra logica de datos por una parte y para la presentacin en otra. Tambin puede ayudarnos a hacer que nuestra aplicacin sea ms mantenible con el paso del tiempo - hace ms difcil que aadamos parte de la lgica en la capa de presentacin.

Cuando implementamos los tres mtodos de accin del controlador en la clase ProductsController, usaremos los parmetros de la URL para obtener los objetos adecuados de nuestra base de datos, y entonces seleccionamos una vista adecuada para generar una resputesta HTML. Usaremos uno de los mtodos RenderView() de la clase base Controller para espcificar la vista que queramos usar, as como explicitamente le pasaremos los datos especficos que queremos que la vista use para renderizar la respuesta. Aqu tenis el resultado final de la clase ProductsController:

Fjos que el nmero de lneas de codigo que tienen los mtodos de accin es muy poco (dos lneas cada uno). Esto es debido a que la lgica de paso de parametros de las URLs es administrada completamente por el framework MVC (ahorrndonos el tener que escribir todo ese cdigo). Tambin es debido a que el escenario de navegacin entre productos es muy simple desde el punto de vista de la lgica de negocio (los mtodos de accin son slo escenarios de lectura). En general nos encontraremos con los llamados "skinny controllers" (controladores flacos) - haciendo referencia a aquellos controladores cuyos mtodos de accin no tienen ms de 10 lneas de cdigo. Esto suele ser una seal de que hemos encapsulado muy bien la lgica de datos y factorizado la lgica de controlador bien. Tests unitarios de la clase ProductsController Os sorprender ver que el siguiente paso es trabajar en el testing de la lgica de la aplicacin y la funcionalidad. Preguntaris cmo es posible? No hemos implementado nada en las vistas y nuestra aplicacin a da de hoy no renderiza ni un tag HTML. Bueno, una de las cosas que hace que el MVC sea interesante es que nos permite hacer test unitarios de los controladores y del model lgico de las aplicaciones completamente independiente de la generacin de las vistas/Html. Como ahora veremos podemos hasta testear esto ntes de crear las vistas. Para hacerle los test unitarios a la clase ProductsController aadiremos una clase nueva ProductsControllerTester al proyecto de Test que se cre en la solucin por defecto cuando creamos el proyecto en Visual Studio:

Ahora definiremos un test simple que comprueba la accin Detail:

El framework ASP.NET MVC ha sido diseado especialmente para que sea fcil hacer test unitarios. Todas las APIS y contratos del framework son interfaces, y los puntos de extensin estn disponibles para permitir una injeccin y personalizacin fcil de objetos (incluyendo la habilidad de usar contenedores IOC como Windsor, StructureMap, Spring.NET y ObjectBuilder). Los desarrolladores sern capaces de usar las clases incluidas, o cualquier tipo de framework .NET para simular sus propios test a los objetos de MVC. En el test anterior, podemos ver un ejemplo sobre cmo inyectar una implementacion tonta de "ViewFactory" en nuestra clase ProductsController antes de llamar al mtodo de accin Detail(). De esta forma estamos sobreescribiendo la factora de vistas por defecto que se usara para renderizar nuestra vista. Podemos usar esta implementacin de factora de vistas para aislar el testeado del comportamiento del mtodo Detail de ProductsController (y no involucrar ninguna vista para hacer esto). Fijaos cmo podemos usar las tres sentencias Assert despus del mtodo Detail() para verificar el correcto comportaminto d este (especficamente que la accin ha obtenido el producto correcto y se lo ha pasado a la vista). Debido a que podemos simular cualquier objeto del framework MVC (incluso objetos IHttpRequest y IHttpResponse), no tenemos que ejecutar tests en el contexto del servidor web actual. Si no que, podemos crear nuestro controlador ProductsController en una librera y testearla directamente. Esto puede acelerar la velocidad de ejecucin de test unitarios, y simplificar la configuracin y la ejecucin de ellos. Si usamos Visual Studio 2008, podemos hacer un seguimiento de los test ejecutados (esta funcionalidad est incluida en VS 2008 Professional):

Creo que os daris cuenta la facilidad que es realizar test con ASP.NET MVC Framework, y nos permite un bonito workflow TDD. Renderizando UI con Vistas Hemos terminado de implementar y testear la aplicacin y la lgica de datos para la seccin de navegacin entre productos de nuestra aplicacin d e-comerce. Ahora tenemos que implementar una interfaz de usuario HTML para ella.

Lo haremos implementando unas "Vistas" que renderizarn la interfaz de usuario adecuada usando objetos de datos relacionados con las vistas que los mtodos de accin de la clase ProductsController proporcionarn cuando se llame al mtodo RenderView():

En este cdigo vemos que el parmetro "Categories" est indicando el nombre de la vista que queremos renderizar, y el segundo parmetro es una lista de objetos Category que queremos pasarle a la vista para que genere el HTML adecuado. ASP.NET MVC tiene la habilidad de usar cualquier motor de templates para ayudarnos a generar la interfaz de usuario (incluyendo motores de templates como NVelocity, Brail - y cualquiera que escribamos ). Por defecto ASP.NET MVC usa las pginas ASP.NET (.aspx), Master Pages (.master) y UserControls (.ascx). Usaermos el motor de ASP.NET para implementar la interfaz de usuario de nuestra aplicacin. Definiendo el archivo Site.Master

Como vamos a estar creando varias pginas para nuestro sitio, empezaremos definiendo una master page que usaremos para encapsular el HTML comn que usaremos en todo el sitio. Esto lo haremos con un archivo llamado "Site.Master" que crearemos en el directorio ViewsShared:

Podemos referenciar una CSS externa para encapsular todos los estilos del sitio, y usar la master page para definir el aspecto del sitio, as como identificar los placeholders que las pginas rellenaran con su contenido propio. Podemos usar todas las caractersticas del diseador de VS 2008 para hacer esto (incluyendo la pantalla partida , el soporte de css, y el soporte de master pages anidadas):

Entendiendo la estructura del directorio /Views Cuando creamos un proyecto para ASP.NET MVC en Visual Studio, crear un subdirectorio llamado "Shared" en el directorio "Views". Este es el lugar recomendado para guardar las Master Pages, User Controls, y las Vistas que queramos compartir entre varios Controladores de nuestra aplicacin. Cuando creamos vistas que son especficas para un slo controlador, la convencin que se usa por defecto en ASP.NET MVC es guardarlas en subdirectorios del directorio /Views. El nombre por defecto de ese subdirectorio debe ser el mismo que el nombre del controlador. Por ejemplo, como ya tenemos una clase controlador llamada "ProductsController", por defecto, guardaremos las vistas asociadas a ella en el directorio /Views/Product:

Cuando llamemos al mtodo RenderView (string viewName) de un controlador concreto, MVC framework buscar primero a su correspondiente .aspx o .ascx vajo el directorio /Views/NombreControllador y si no puede encontrarlo comprobar el directorio ViewsShared. Crear la vista de Categoras Podemos crear una vista "Categories" para ProductsController con Visual Studio usando el men "Add New Item" en el directorio Products y seleccionando "MVC View Page". ESto crear un nuevo .aspx que podemos asociar a la pgina Site.Master para que tenga el mismo look and feel del sitio (al igual que con las master pages tener un soporte WYSIWYG):

Cuando creamos aplicaciones con el patron MVC, querremos mantener el cdigo de las vistas lo ms simple posible, y asegurarnos de que ese cdigo slo se encarga de renderizar la interfaz de usuario. La lgica de la aplicacin y de los datos slo debera estar escrito en las clases controlador. Estas clases pueden elegir pasar los objetos necesarios para renderizar una vista cuando llamen al mtodo RenderView. Por ejemplo, aqu tenes el mtodo de accin de Categories de nuestra clase ProductsController que le pasa una coleccin List de objetos categora:

Las pginas de vistas de MVC derivan de System.Web.Mvc.ViewPage que nos proporciona un conjunto de mtodos de ayuda y propiedades que podemos usar para la generacin de la interfaz de usuario. Una de esas propiedades es "ViewData", y nos da acceso a objetos de datos especficos de la vista que el controlador le pasa como argumentos al mtodo RenderView(). Desde la vista podemos acceder a "ViewData" tanto de forma tipada como no tipada. Si nuestra vista deriva de ViewPage, la propiedad ViewData se tipar como un diccionario. Si la vista deriva de tipos genricos ViewPage<T> - donde T indica el tipo de datos que el controlador le pasa a la vista entonces la propiedad ViewDAta es fuertemente tipada. Por ejemplo, en el cdigo trasero de nuestra vista de Categoras vemos que deriva de ViewPage<T> - donde le indicamos que T es una List de objetos Category:

Esto implica que tenemos seguridad de tipos, intellisense y comprobaciones en tiempo de compilacin en el cdigo de la vista cuando trabajamos con los datos de ProductsController.Categories:

Renderizando la vista de Categorias: Si recordamos las capturas de pantalla del principio del post, queremos mostrar la lista de productos de una categora en la vista de Categoras:

Podemos escribir esta generacin de la interfaz de usuario de dos formas: 1) Usando cdigo inline en el .aspx o 2) Usando controles de servidor en el .aspx y enlazarlos desde el cdigo trasero. Primera opcion: Cdigo inline Las pginas ASP.NET, los User Controls y las Master Pages soportan la sintaxis de <% %> y <%= &> para insertar cdigo en el rendering del html. Podemos usar esta tcnica con nuestra vista de categoras para escribir un bucle foreach que genere la lista en html:

VS 2008 nos da intellisense completo en el editor de cdigo tanto para VB como para C#.

Tambin nos permite debugear el cdigo inline (permitindonos poner puntos de ruptura):

Segunda opcin: Usando controles de servidor Las pginas ASP.NET, los User Controls y las Master Pages tambin tienen la habilidad de usar contorles de servidor declarativos para encapsular la generacin del HTML. En lugar de usar el cdigo inline, podemos usar el nuevo control <asp:listview> de .NET 3.5 para generar la lista:

Fijaos cmo el control ListView encapsula tanto el renderizado de los valores como administra el escenario cuando no hay elementos en la lista (el <EmptyDataTemplate> nos salva de tener que escribir una sentencia if/else en el lenguaje de marcado). Ahora podemos enlazar nuestros objetos de categora al control listview en el cdigo trasero:

Importante: En el mundo MVC slo querremos poner lgica de renderizado en el cdigo trasero de las vistas (y ninguna lgica de la aplicacin). Arriba, la nica lgica que tenemos es la asignacin de la coleccin fuertemente tipada de ViewData al control ListView. El controlador ProductsController es el nico que obtiene la lista de categoras de la base de datos, no la vista. El control ListView de la vista generar el mismo HTML que el cdigo inline de la primera opcin. Como no queremos tener un control <form runat="server"> en la pgina, sin viewstate, valores ID, no se emitir nada. Slo CSS y HTML:

El mtodo Html.ActionLink Una de las cosas de las que os habris dado cuenta tanto en la opcin del cdigo inline como en la versin con un control de servidor son las llamadas al mtodo Html.ActionLink:

El objeto Html es una propiedad de ayuda en la clase base ViewPage, y el mtodo ActionLink es una forma de hacer sencillo el generar enlaces HTML que enlacen a los mtodos de accin de los controladores. Si miramos la salida HTML podemos ver un ejemplo del HTML generado por este mtodo: <a href="http://weblogs.asp.net/Products/List/Beverages" mce_href="http://weblogs.asp.net/Products/List/Beverages">Beverages</a> Esta firma del mtodo Html.ActionLink que estamos usando es de esta forma: string ActionLink(string text, object values); El primer parmtero representa al contenido del enlace a renderizar (por ejemplo: <a> el texto que va aqu </a>. El segundo argumento es un tipo annimo que represetna una secuencia de valores a usar para generar la URl (podeis pensar en ello como una forma limpia de generar diccionarios). Lo veremos en ms detalle en un post futuro sobre el motor de rutado de URL. Resumiendo, es que podemos usar el sistema de rutado URL tanto para procesar las URLs entrantes como para generar las urls que emitiremos. Si tenemos una rgla como esta: /<controller>/<action>/<category> Y luego escribir este cdigo en la vista de categoras del ProductController: <%= Html.ActionLink("Click Me to See Beverages", new { action="List", category="Beverages" } %>

El mtodo ActionLink usar las reglas de mapeo de url de nuestra aplicacin para enmascararla en los parmetros y generar esta salida: <a href="http://weblogs.asp.net/Products/List/Beverages" mce_href="http://weblogs.asp.net/Products/List/Beverages">Click Me to See Beverages</a> Esto hace muy fcil a nuestra aplicacin generar URLs y callbacks de AJAX en nuestros Controladores. Tambin implica el poder actualizar las reglas de rutado de URLs en un lugar y tener el cdigo disponible en toda nuestra aplicacin. Nota importante: Para ayudar la testeabilidad, el framework MVC no soporta los eventos de postback directamente a controles de servidor en las vistas. En lugar de eso, las aplicaiones ASP.NET MVC generar hyperlink y callbacks AJAX para las acciones del controlador - y usa las vistas (y cualquier control de servidor) slo para renderizar la salida. Esto asegura que la lgica de Vistas sigue siendo mnima y slo se centra en la renderizacin, pudiendo as testear las clases controlador y comprobar toda la lgica de datos y de la aplicacin independientmente de las vista. Lo veremos en otros post futuros. Resumen Este primer post es muy largo, pero ofrece un buen primer vistazo sobre los diferentes componentes que ofrece ASP.NET MVC, y cmo podemos crear un escenario real tpico. Los primeros bits de ASP.NET MVC estar disponible en unas pocas semanas, y podris usarlo para hacer todos los pasos de arriba. Aunque la mayora de los conceptos de MVC (en particular la idea de separar conceptos) sean nuevos a muchos lectores, este post tambin ha mostrado que la implementacin de ASP.NET MVC en la que estamos trabajando es bastante limpia. Podemos usar .ASPX, .ASCX y MASTER PAGES y ASP.NET AJX para crear las vistas de ASP.NET MVC. Caractersticas no relacionadas con la interfaz de usuario de ASP.NET como Autenticacin por formulario, Windows Authentication, Membership, Roles, URL Authorization, Cacheo, Session STate, Profiles, Health Monitoring, Configuration, Compilation, Localization, y HttpModules/HttpHandlers soportan el modelo MVC. Si no os gusta el modelo MVC o no lo encontrais natural para vuestra forma de desarrollo, no tendreis que usarlo. Es una oferta totalmente opcional - y no reemplaza al modelo de WebForms Page Controller. Tanto WebForms como MVC estarn soportados y extendidos. Incluso podis crear una aplicacin y tener partes escritas con WebForms y partes con MVC si queris. Si os ha gustado lo que habis visto de MVC (o si queris aprender ms), estad atentos al blog en las prximas semanas. Escribir sobre ms conceptos de MVC y los usaremos para crear nuestra aplicacin de e-commerce para ver ms caractersticas. Espero que sirva

ASP.NET MVC Framework (2 Parte): URL Routing


1. El mes pasado escrib el primer post de una serie en la que veremos el nuevo ASP.NET MVC Framework. En aquel post vismo cmo crear un escenario simple en el que creamos un sitio para navegar por la lista de productos y libreras. Cubrimos los conceptos de alto nivel que hay detrs de MVC, y demostramos cmo crear un proyecto ASP.NET MVC de la nada hasta implementarlo y testear la funcionalidad de listado de productos.

En el post de hoy nos vamos a meter ms en la arquitectura de rutado de ASP.NET MVC Framework, y veremos algunas cosas que podemos usar en escenarios ms avanzados. Recapitulando de la primera parte En la primer post, creamos un sitio de e-comerce que expona tres tipos de urls: URL Format /Products/Categories /Products/List/Category /Products/Detail/ProductID Behavior Browse all Product Categories List Products within a Category Show Details about a Specific Product URL Example /Products/Categories /Products/List/Beverages /Products

Manejbamos estas URLs creando la clase "ProductsController":

Una vez que aadimos esta clase a nuestra clase, ASP.NET MVC administra automticamente las URLs entrantes y las ruta al mtodo de accin al controlador correspondiente.

En el post de hoy vamos a meternos en cmo se hace este mapeo de URLs, y exploraremos escenarios ms avanzados de rutado en los que nos aprovecharemos de las posibilidades de ASP.NET MVC. Tambin demostrar lo fcil que es hacer test unitarios en estos escenarios. Qu hace el sistema de rutado de URLs de ASP.NET MVC? El framework ASP.NET incluye un sistema flexible de rutado de urls que nos permite definir reglas de mapeado en nuestras aplicaciones. El sistema de rutado tiene dos objetivos principales: 1. 2. Mapear urls entrantes a la aplicacin y rutarlas al controlador y mtodo de accin correctos. Crear urls de slaida que puedan ser usadas para volver a llamar a Controladores/Actiones (por ejemplo: desde un enlace <a href="" mce_href="">, y llamadas AJAX).

Poder usar reglas de mapeado de url para urls entrantes y salientes nos da una gran flexibilidad. Es decir, si queremos cambiar ms adelante la estructura de urls de nuestra aplicacion (por ejemplo: cambiar el nombre /Products a /Catalog), podemos hacerlo modificando un conjunto de reglas de mapeo en el nivel de aplicacin - sin tener que cambiar ninguna lnea de cdigo de los controladores o vistas. Reglas de rutado por defecto de ASP.NET MVC Cuando usamos Visual Studio para crear una aplicacin con el template de "ASP.NET MVC Web Application" se aade una clase ASP.NET Application. Esto est implementado en el cdigo trasero de Global.asax:

Esta clase permite a los programadores manejar el inicio/apagado de la aplicacin y la administracin de errores. El template por defecto aade el mtodo Application_Start a la clase y registra dos reglas de rutado asociadas:

La primera regla indica que ASP.NET MVC debera mapear las urls a los controladores usando el formato "[controlador]/[accin]/[id]" para determinar la clase controlador al que instanciar, y qu mtodo de accin invocar (junto a los parmetros necesarios). Esta regla por defecto es el motivo de que nuestra peticin /Products/Detail/3 en el ejemplo del primer post invoca al mtodo de detallles de la clase ProductsController y le pasa un 3 como argumento:

La segunda regla se aade para un caso especial: "Default.aspx", la raz de nuestra aplicacin (que normalemente pasan los servidores web en lugar de "/" cuando administran la raiz de las urls de una aplicacin). Esta regla garantiza que las peticiones a "/Default.asx" "/" se manejan por la accin "Index()" de la clase "HomeController" (es un controlador que se aade automticamente por Visual Studio cuando se crea un proyecto "ASP.NET MVC Web Application"). Comprendiendo las intancias de rutado Las reglas de rutado se registran aadiendo instancias Route en la coleccin System.Web.Mvc.RouteTable. La clase Route define unas propiedades que podemos usar para configurar las reglas de mapeo. Podemos configurar estas propiedades con las asiganciones tradicionales de .NET 2.0:

O aprovechandonos de la inicializacin de objetos de los compiladores de C# y VB:

La propiedad Url de la clase Route define la regla que debe ser usada para comprobar si una regla de rutado se aplica a una peticin entrante. Tambin define cmo debe "tokenizarse" para los parmetros. Estos parmetros se definen con la sintaxis "[ParamName]". Como veremos ms tarde, no estamos restrinjidos a usar un conjunto de parametros "bien conocidos" - podemos tener un nmero arbitrario de parmetros. Por ejemplo, podemos usar una regla "/Blogs/[Username]/Archive/[Year]/[Month]/[Day]/[Title]" para tokenizar las urls entrantes a los posts - y haremos que MVC Framework parsee y apase el Username, year, month, day y title como parmetros al mtodo de accin del controlador. La propiedad "Defaults" define un diccionario con valores por defecto a usar en el evento de la url entrante si no incluye alguno de los parmetros especificados. Por ejemplo, en los casos anteriores estamos definiendo dos valores por defecto - uno para "[action]" y otro para "[id]". Con esto si recibimos una url con /Products/ el sistema de rutado usar "Index" como nombre de la accin de ProductsController a ejecutar. Si se especific /Products/List se pasar una cadena nula como parmetro ID. La propiedad "RouteHandler" define la instancia IRouteHandler que debe usarse para procesar la peticin despus de que la URL es "tokenizada" y se determina cul es la regla de rutado. En los ejemplos anteriores estamos indicando que queremos usar la clase System.Web.Mvc.MvcRounteHandler para procesar las URLs. El motivo de este paso extra es que queremos asegurarnos de que el sistema de rutado de URLs puede usarse tanto para peticiones MVC como NO-MVC. Con la interfaz IRouteHandler somos capazes de usar de forma limpia peticiones NO-MVC (como para los WebForms, soporte Astoria REST, etc). Tambin hay una propiedad "Validation" que veremos ms tarde. Esta propiedad nos permite especificar precondiciones necesarias para encontrar la regla de rutado adecuada. Por ejemplo, podemos indicar que una regla de rutado slo se aplicara a un verbo especfico HTTP (pudiendo mapear as comandos REST), o podramos usar expresiones regulares en los argumentos para ver qu regla es la adecuada. Nota: En la primera preview pblica de MVC la clase Route no es extensible . Estamos viendo si la haremos extensible en la prxima versin y permitir a los desarrolladores aadir reglas especficas (por ejemplo: una clase RestRoute) para aadir semntica adicional y funcionalidad. Evaluacion de reglas de rutado Cuando llega una peticion URL a una aplicacion ASP.NET MVC, el framework MVC evalua las reglas de la coleccin RouteTable.Routes para determinar el controlador apropiado para manejar la peticin. MVC elige el controlador a usar evaluando las reglas de RouteTable en el orden el que fueron registradas. La URL es comrobada con cada regla y cuando encuentra una que sea adecuada nos dir el RouteHandler que procesar la peticin (y todas las dems reglas se ignoran). Con esto, la mejor forma de estructurar las reglas es de ms especfica a menos. Escenario de rutado: Busqueda personalizada de urls Vamos a ver ahora el uso de reglas de rutado personalizadas en un escenario real. Vamos a implementar la bsqueda en nuestro sitio de e-comerce. Aadimos la clase SearchController al proyecto:

Ahora definiremos dos mtodos de accin. Usaremos el mtodo Index() para presentar una pgina de bsqueda que tenga un textbox para que se introduzca el texto a buscar. La accin Results() se usar para realizar la bsqueda contra la base de datos y mostrar los resultados al usuario:

Usando la regla de mapeado por defecto /[controller]/[action]/[id] , podramos usar las urls siguientes para invocar las acciones del SearchController: Scenario Search Form: Search Results: URL /Search/ /Search/Results?query=Beverages /Search/Results?query=ASP.NET Action Method Index Results Results

Fijos que la razn por la que la raz de /Search por defecto mapea la accin Index() ya que la definicin /[controller]/[action]/[id] se crea por defecto cuando Visual Studio crea un nuevo proyecto:

Si bien URLs como Search/Results?query=Beverages son perfectamente funcionales, podemos querer URLs algo ms "bonitas" para los resultados de bsqueda. Concretamente podramos querer quitar el nombre de la accin de la URL "Results", y pasarla en la consulta como parte de la URL en lugar de usar un argumento QueryString. Por ejemplo: Scenario Search Form: Search Results: URL /Search/ /Search/Beverages /Search/ASP.NET Action Method Index Results Results

Podemos permitir estas URLs "bonitas" aadiendo dos reglas de mapeado ntes de la de por defecto /[controller]/[action]/[id]:

Con estas dos reglas estamos especificando explcitamente los parmetros del Contorlador y de la Accin para /Search/URLs. Estamos indicando que "/Search" siempre ser manejado por la accin "Index" en el SearchController. Cualquier URL con una jerarqua as (/Search/Foo, /Search/Bar, etc) siempre ser manejada por la accin "Results" en el SearchController. La segunda regla indica que cualquier prefijo de /Search/ debe tratarse como parmetro "[query]" que ser pasado al mtodo de accin:

Lo ms comn es que permitados unos resultados de bsquedas paginados (donde slo mostraremos los 10 resultados de una vez). Esto lo podemos hacer a travs de un argumento querystring (por ejemplo: /Search/Beverages?page=2) o podemos embeber el ndice de la pgina como parte de la URL (Por ejemplo: /Search/Beverages/2). Para permitir esto lo que tenemos que aadir es un parmetro opcional extra a la segunda regla:

Fijos cmo ahora la regla es "Search/[query]/[page]". Tambin hemos configurado el ndice por defecto a 1 en los casos en los que no se incluya en la url (esto se hace a travs de los tipo annimo como valores por defecto). Ahora podemos actualizar el mtodo de accin SearchControler.Results para que tome este parmetro de la pgina como un argumento:

Y con esto tenemos una URL "bonita" para las bsquedas de nuestro sitio (todo lo que queda por implementar es el algoritmo de bsqueda que dejaremos como ejercicio para el lectos <g>). Validacin de precondiciones para reglas de rutado Como ya he mencionado ntes, la clase Route tiene la propiedad "Validation" que nos permite aadir precondiciones a validar para las reglas (a parte de los filtros de URL). El framework ASP.NET MVC nos permite usar expresiones regulares para validar cada parmetro en la URL, as como evaluar las cabeceras HTTP (para rutar URLs vasndonos en verbos HTTP). Aqu tenis una validacin personalizada que podramos crear para las urls del tipo "/Products/Detail/43". Especifica que el argumento ID debe ser un nmero (no un string), y que debe tener entre 1 y 8 caracteres:

Si le pasamos la url /Products/Detail/12 a nuestra a plicacin, la regla anterior ser vlida. Si le pasamos /Products/Detail/abc o /Products/Detail/23232323232323 no lo ser. Crear Urls de salida desde el sistema de rutado Al principio del post dije que el sistema de rutado de urls de MVC era responsable de dos cosas: 1. 2. Mapear urls entrantes a Controladores/Acciones Ayudar en la construccin de URLs salientes que puedan ser usadas par ahacer call backs a los Controladores/Acciones (por ejemplo, desde posts, enlaces <a href="">, llamadas de AJAX).

El sistema de rutado de URLs tiene una serie de mtodos de ayuda y clases que hacen fcil crear URLs en tiempo de ejecucin (tambin podis tener URLs trabajando con la coleccin RouteTable.Route directamente. Html.ActionLink En la primera parte de esta serie resum un poco el mtodo Html.ActionLink(). Podemos usarlo en las vistas y permitir la generacin de enlaces <a href="">. Lo interesante es que estas URLs se generan a partir de las reglas de mapeo del sistema e rutado. Por ejemplo, las dos llamadas a Html.ActionLink siguientes:

cojen automticamente los resultados de la regla de bsquedas que configuramos antes y el atributo "href" se genera automticamente :

Fijos en cmo la segunda llamada al mtodo Html.ActionLink mapea el parmetro de la "pagina" como parte de la URL (y fijos en que la primera omite el valor - ya que sabe cual es el valor por defecto en el lado del servidor). Url.Action Adems de usar el mtodo Html.ActionLink, ASP.NET MVC tambin tiene este segundo mtodo. Genera un conjunto de strings de URLs - que podemos usar donde queramos. Por ejemplo, el code snippet siguiente:

puede usarlo el sistema de rutado para devolver una url.

Controller.RedirectToAction ASP.NET MVC tambin tiene este tercer mtodo que podemos usar en los controladores para crear redirecciones (donde las URLs son generadas a partir del sistema de rutado de URLs) Por ejemplo cuando invocamos al mtodo siguiente en un controlador:

internamente genera una llama a Response.Redirect("/Search/Beverages") DRY Lo bonito de estos mtodos es que nos permiten no tener un cdigo enrevasdo en los paths de las URL en la lgica de los controladores y vistas. Si ms adelante decidimos cambiar el mapeo de la ruta URL de la bsqueda de "/Search/[query]/[page]" a "/Search/Results/[query]/[page]" o /Search/Results?query=[query]&page=[page]" podemos hacerlo fcilmente editndolo slo en un lugar (en el cdigo de registro de rutas). No tenemos que cambiar ningn cdigo de nuestras vistas ni controladores (esto sigue el principo DRY Creando Urls de salida desde el sistema de rutado (con expresiones lambda) Los ejemplos anteriores usan el nuevo soporte de tipos annimos de VB y C# de VS 2008. En los ejemplos estamos usando los tipos annimos para pasar la secuencia de nombres/valores para usarlos para mapar URLs(podis pensar en esto como una forma limpia de generar diccionarios). Adems de para parmetros de forma dinmica usando tipos annimos, el framework ASP.NET MVC tambin tiene la habilidad de crear acciones de rutas usando un mecanismo fuertemente tipado que nos da en tiempo de compilacin intellisense para ayudarnos con las URLs. Esto lo hace con tipos genricos y el soporte de VB y C# para expresiones Lambda. Por ejemplo, el tipo annimo de la llamada ActionLink:

Tambin puede escribirse as:

Adems de ser fcil de escribir, la segunda opcin tiene el beneficio de que es type-safe, lo que quiere decir que tenemos checkeo en tiempo de compilacion e intellisense en Visual Studio (tambin podemos usar herramientas de refactoring):

Fijos cmo podemos usar el intellisense para seleccionar el mtodo Action del SearchController que queramos usar - y cmo los parmetros son fuertemente tipados. Las urls generadas son todas del sistema de rutado de ASP.NET MVC.

Os estaris preguntando - Como nos aseguramos de que esto funciona? Si recordis, hace ocho meses escrib sobre las expresiones Lambda. Hablaba sobre cmo se compilan las expresiones lambda como un delegado, as como con un rbol de objetos de expresin que pueden ser usadas en tiempo de ejcucin para analizar expresiones lambda. Con el mtodo Html.ActionLink<T> usamos la opcin de este rbol de expresiones y se analizan las lambda en tiempo de ejecucin buscando el mtodo de accin que invoca as como los tipos de los parmetros, nombres y valores que se especifican en la expresin. Podemos usar esto en el sistema de rutado de MVC para devolver la url adecuada y asociarle el HTML. Importante: Cuando usamos estas expresiones lambda nunca ejecutaremos la accin del controlador. Por ejemplo, el siguiente cdigo NO invoca el mtodo de accin "Results" en el SearchController:

Si no que devuelve este link html:

Cuando se hace clic en este link mandar una peticin http al servidor que invocar el mtodo "Results" del SearchController. Tests unitarios de rutas Uno de los principios del diseo del framework ASP.NET MVC es permitir un soporte para test. Como todo el framework MVC, podemos hacer test unitarios sobre rutas y reglas de rutado. El sistema de rutado de MVC puede ser instanciado y ejecutado independientemente de ASP.NET - con esto podemos cargar y testear patrones de rutas con cualquier librera de test (no hay que ejecutar ningn servidor web) y usar cualquier framework (NUnit, MBUnit, MSTest, etc). Aunque podemos hacer test unitarios con la coleccin de RouteTable de una aplicacin ASP.NET MVC directamente, en general no es buena idea tener test unitarios que se basen en estados globales. Un mejor patrn que podemos usar es estructurar la lgica de registro de rutas en un mtodo RegisterRoutes() como el siguiente que trabaja contra el RouteCollection que se pasa como argumenteo (nota: probablemente crearemos un template para VS en la prxima actualizacin):

Ahora podemos escribir test unitarios con la instacia de nuestra RouteCollection y llamar al mtodo Registerroutes() de nuestra aplicacin. Y podremos simular peticiones a la aplicacin y comprobar que se registran los controladores y vistas adecuados - sin tener que preocuparnos en ningn efecto colateral:

Resumen Este post nos ensea unos cuantos detalles sobre cmo funciona la arquitectura de rutado de ASP.NET MVC, y cmo podemos usarla para personalizar la estructura y aspecto de las urls que publicaremos en nuestras aplicaciones ASP.NET MVC. Por defecto cuando creamos una aplicacin web ASP.NET MVC se predefinir la regla de rutado /[controller]/[action]/[id] que podemos usar (sin tener que configurar nada). ESto nos permite crear muchas aplicaciones sin tener que registrar reglas de rutado personalizadas. Pero hemos demostrado que si queremos podemos crear nuestros propios formatos de URL y que no es difcil - y que el MVC framework nos permite una gran flexibilidad para hacer esto. Espero que sirva. Scott.

ASP.NET MVC Framework (3 Parte): Pasando ViewData desde Controladores a las Vistas
En estas semanas he estado trabajando en una serie de post sobre ASP.NET MVC Framework. Es una aproximacin opcional que podis usar para estructurar vuestras aplicaciones ASP.NET para separar claramente los diferentes aspectos de esas aplicaciones, y hacer mucho ms sencillo realizar test en vuestro cdigo. En el primer post creamos una aplicacin de e-comerce sencilla para navegar/listar los productos. Vimos los conceptos de ms alto nivel que hay detrs de MVC, y vimos cmo crear un nuevo proyecto ASP.NET MVC desde cero hasta implementar y testear esas funcionalidades. El segundo post de la serie se introducia un poco ms en la arquitectura de rutado de URLs, y vimos cmo funciona y algunos escenarios de rutado de URLs avanzados. En el post de hoy veremos cmo interactan los Controladores con las Vistas, y veremos cmo pasar datos desde un Controlador a una Vista para que renderize la respuesta al cliente. Recapitulemos un poco En el primer post creamos un sitio de e-comerce que nos permita navegar y listar los poductos. Lo implementamos con ASP.NET MVC Framework, lo que nos llev a estructurar el cdigo de una manera natural para distinguir entre Controladores, modelos y vistas. Cuando un navegador envia una peticin HTTP al sitio web, ASP.NET MVC usar su motor de rutado de URL para mapear la peticin a un mtodo de accin de una clase controlador que lo procesar. Los controladores de MVC son los responsables de procesar las peticiones entrantes, manejar las entradas de usuarios y sus interacciones, y de ejecutar la lgica de la aplicacin (obteniendo y actualizando el modelo de datos, etc). Cuando lleva tiempo renderizar la respuesta HTML al cliente, los controladores usan los componentes llamados "Vistas" - que estn implementados como clases separadas de los controladores, y se encargar de encapsular toda la lgica de presentacin.

Las vistas NO DEBEN contener ninguna lgica de la aplicacin ni ningn cdigo de acceso a datos, sino que toda la lgica de la aplicacin y de datos slo puede estar en las clases controlador. La motivacin que hay detrs de esta distincin es para ayudar a mantener una serparcin clara de la lgica de aplicacin, datos y de la interfaz de usuario. Esto hace ms sencillo crear test unitarios en cada una de las partes de la aplicacin. Las vistas SOLO DEBEN renderizar la salida usando unos datos especficos pasados por la clase Contorlador. En ASP.NET MVC llamamos a esos datos "ViewData". El resto del post cubrir algunas aproximaciones diferentes que podemos usar para pasar estos "ViewData" del controlador a la Vista. Un escenario simple: listado de productos Para ayudar a comprender algunas de las tcnicas que vamos a usar para pasar ViewData del contorlador a la visa, crearemos una pgina de listado de productos:

Usaremos el entero CategoryID para filtrar los productos que queremos mostrar en la pgina. Fijos cmo estamos metiendo el CategoryID como parte de la URL (por ejemplo: /Products/Category/2 o /Products/Category/4). Nuestra pgina de listado est renderizando dos elementos. El primero es el nombre de la categora que se muestra (por ejemplo: "Condiments"). El segundo es una lista HTML <ul><li/></ul> de nomres de productos. Los he rodeado los dos en rojo. Ahora veremos dos formas diferentes de implementar la clase "ProductsController" que procesen la peticin, obtenga los datos, y se los pase a la vista "List" para renderizarlo. En la primera forma usaremos un objeto dicionario enlazado a los datos. En la segunda uramos una clase fuertemente tipada. 1: Pasando ViewData con el Diccionario Controller.ViewData La clase base Controller tienen una propiedad diccionario "ViewData" que podemos usar para meter los datos que queremos pasarle a la Vista. Aadimos objetos a este diccionario con un patrn clave/valor. Aqu tenis la clase ProductsController con un mtodo de accin "Category" que implementa el listado de producots. Fijos cmo se usa el parmetro ID de las categoras para buscar el nombre de la categora, y cmo obtenemos la lista de productos de esa categora. Est guardando esos datos en la coleccin Controller.ViewData usando "CategoryName" y "Products":

La accin Category llama al mtodo RenderView("List") para indicar qu plantilla queremos renderizar. Cuando llamamos a RenderView de esta forma, le pasar el diccionario ViewData a la Vista. Implementando nuestra Vista Implementaremos la vista List usando el archivo List.aspx que est bajo el directorio ViewsProducts. Esta pgina heredar de la masterpage Site.Master.MasterPage del directorio ViewsShared (clic con el botn derecho en VS 2008 y seleccionamos Add New Item -> MVC View Content Page para crear una master page cuando creemos una nueva pgina de vista:

Cuando creamos la pgina List.aspx con la plantilla MVC View Content Page no deriva de la clase System.Web.UI.Page, sino que lo hace de System.Web.Mvc.ViewPage (que es una subclase de al clase Page):

La clase ViewPage nos proporciona una propiedad "ViewData" que podemos usar en la pgina para acceder a los objetos que hayan sido aadidos por el Controlador. Entonces podemos cojer estos objetos y usarlos para renderizar la salida HTML usando tanto controles de servidor como los tags <%= %> Implentando la vista con controles de servidor. Aqu tenis un ejemplo sobre cmo usar <asp:literal> y <asp:repeater> para implementar la salida HTML:

Podemos enlazar el ViewDAta a estos controlres con el siguiente cdigo en la clase del cdigo trasero (fijos cmo usamos el diccionario ViewData):

Nota: como no tenemos un <form runat="server"> en la pgina, no se emitir ningn view-state. Los controles siguientes tampoco renderizan ningn valor ID - con lo que tenemos control total sobre el control HTML. Implementando la vista usando <%= %> Si prefers usar cdigo inline para generar la salida, lo podemos hacer de la siguiente manera:

Nota: Como el diccionario ViewData contiene objetos del tipo "object" tenemos que hacer un casting de ViewData["Products"] a una lista List<Product> o a un IEnumerable<Product> para poder usar la sentencia foreach. Estamos importando los namespaces System.Collections.Generic y MyStore.Models en la pgina para no tener que escribirlo una y otra vez en List<T> y en el tipo Products. Nota: El uso de la palabra "var" es un ejemplo de uso de la inferencia de tipos de C# y VB de VS 2008 (leed este post sobre el tema). Como tenemos que hacer un cast de ViewData["Products"] a List<Product> tendremos intellisense completo en las variables de Product en el archivo List.aspx:

2. Pasando ViewData con Clases Fuertemente Typadas Tambin podemos pasar estos ViewData a travs de objetos fuertemente tipados desde los Controladores a las Vistas. Hay un par de ventajas usando esta forma: 1. 2. 3. 4. No tenemos que usar strings para buscar objetos, y tenemos chequeo en tiempo de compilacin tanto en el cdigo de los Controladores como en las Vistas. No tenemos que hacer castings especficos de los objetos del diccionario cuando usemos un lenguaje fuertemente tipado como C#. Tendremos intellisense automtico en los objetos ViewData tanto en el .aspx como en el cdigo trasero. Podremos usar herramientas de refactoring para automatizar cambios en la aplicacin y en los test.

Aqu tenis una clase fuertemente tipada "ProductsListViewData" que encapsula los datos necesarios para la vista List.aspx. Tiene dos propiedades CategoryName y Products (implementados usando el soporte de c# de las propiedades automticas):

Ahora podemos actualizar la implementacin de ProductsController para que use este objeto para pasar un objeto ViewData fuertemente tipado a la vista:

Fijos cmo le pasamos el objeto fuertemente tipado ProductsListViewData aadindolo como un parmetro extra al mtodo RenderView(). Usando el diccionario de ViewData de las vistas como un objeto fuertemente tipado.

En las implementaciones anteriores de List.aspx segurn funcionando con la nueva clase ProductsController - no hace falta ningn cambio. Esto es debido a que cuando el objeto ViewData es fuertemente tipado y lo pasamos a una vista que deriva de la clase ViewPage, el diccionario ViewData usar la reflexin automticamente sobre las propiedades del objeto fuertemente tipado para buscar los valores. As que el cdigo siguiente:

usar la reflexin para obtener el valor de la propiedad CategoryName del objeto ProductsListViewData que le pasamos cuando llamamos al mtodo RenderView. Usando ViewPage<T> como clase base para ViewData fuertemente tipados. Adems de soportar un diccionario como clase base para los ViewPage, ASP.NET MVC tambin tiene una implementacin genrica para ViewPage<T>. Si las vistas derivan de ViewPage<T> - donde T indica el tipo de la clase ViewData que el controlador pasa a la vista - entonces la propiedad ViewData ser fuertemente tipada usando esa clase T. Por ejemplo, podemos actualizar el cdigo trasero List.aspx.cs para que derive de ViewPage, pero de ViewPage<ProductsListViewData>:

Cuando hacemos esto, la propiedad ViewData de la pgina pasar de ser un diccionario al tipo ProductsListViewData. Esto significa que en lugar de usar un diccionario basado en strings para buscar los datos, podemos usar propiedades fuertemente tipadas:

Podemos usar tanto controles de servidor como <%= %> para renderizar el HTML de esos ViewData. Implementar nuestro ViewPage<T> con controles de servidor Aqu tenis un ejemplo de cmo usar los controles <asp:literal> y <asp:repeater> para implementar la interfaz HTML. Es exactamente el mismo contenido que usamos en List.aspx derivada de ViewPage:

Aqu vemos cmo es el cdigo trasero. Fijos que como derivamos de ViewPage<ProductsListViewData> podemos acceder a las propiedades directamente - y no tenemos que hacer ningn casting (tambin podemos aprovecharnos de esto a la hora de hacer refactoring cuando queramos cambiar elnombre de alguna propiedad):

Implementando nuestra ViewPage<T> con cdigo inline <%= %> Si preferimos usar cdigo inline para generar la salida, podemos obtener el mismo resultado usando el siguiente List.aspx:

Usando ViewPage<T> no necesitamos buscar string en los ViewData. Ms importante an, fijos que tampoco tenemos que hacer ningn casting ya que es fuertemente tipado. Esto implica que podemos escribir un foreach (var product in ViewData.Products) sin tener que hacer un casting a Products. Tambin tenemos intelisense en la variable product dentro del bucle:

Resumen

Hemos visto ms detalles sobre cmo los controladores pasan datos a las Vistas para renderizar las respuestas al cliente. Podemos usar tanto diccionarios fuertemente tipados como no. La primera vez que intentamos crear y ejecutar una aplicacin MVC veremos que el concepto de separar la lgica de los controladores de la generacin de la interfaz de usuario puede ser un poco raro. Puede que nos cueste un poco de tiempo hasta que nos sintamos cmodos y hasta que cambiemos nuestra forma de pensar cuando procesamos una peticin, ejecutando toda la lgica de la aplicacin, empaquetando los viewdata necesarios para crear las respuestas HTML y luego tratarlos de forma separada para renderizarlo. Importante: si no os sents cmodos con este modelo entonces nolo usis - el modelo MVC es totalmente opcional, y no creemos que sea algo que todo el mundo deba usar. Los beneficios y las metas tras esta forma de dividir una aplicacin es que nos permite ejecutar y testear nuestras aplicaciones y la lgica de datos a parte de la generacin de la interfaz de usuario. Esto hace ms fcil de desarrollar test unitarios ms comprensibles para nuestra aplicacin, as como usar TDD(test driven development) a medida que creamos aplicaciones. En prximos post veremos esto en ms detalle, y tambin veremos unas buenas prcticas para testear de forma fcil nuestro cdigo.

También podría gustarte