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 característica opcional. Nos aporta un modelo estructurado que se preocupa de separar claramente las diferentes partes de una aplicación, y hace mucho más sencillo la realizacion de test unitarios y soporta un workflow TDD. También 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 montón de preguntas de gente con ganas de aprender sobre el. Debido al nivel de interes creo que podía tener sentido juntar unos cuantos posts que describan cómo usarlo en más detalle. Éste es el primero de una serie de post que escribiré en las proximas semanas.

Una aplicación de E-commerce simple

Vamos a usar una aplicación de e-comerce simple para ilustrar cómo funciona el ASP.NET MVC Framework. Para el post de hoy, implementaremos el listado/navegación de productos de la tienda.

Vamos a crear una tienda que permita a los usuarios navegar por una lista de categorías de productos cuando van a la URL /products/categories:

Cuando un usuario hace clic en una categoría de la página, llegarán a la lista de productos de esa categorías con una url con este formato: Products/list/Nombrecategoría -:

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 separación de conceptos entre los diferentes componentes de la aplicación, y nos permitirá integrar test unitarios y un desarrollo guiado por test.

Crear una aplicación ASP.NET MVC

El ASP.NET MVC Framework incluye una serie de Templates para Visual Studio que hacen fácil crear una aplicación. Sólo con ir al menú File->New Project elegimos "ASP.NET MVC Web Application" tendremos el esqueleto para una aplicación web.

Por defecto, cuando creamos una nueva aplicación de esta forma, Visual Studio creará una nueva solución y añadirá dos proyectos. El primero es un proyecto web en el que implementaremos nuestra aplicación. 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).

También estamos trabajando en templates para NUnit, MBUnit y otros frameworks de test unitarios, de forma que podáis usarlos para crear vuestra aplicación y tener un proyecto de test inmediato para usarlo.

Comprendiendo la estructura de directorios del proyecto

La estructura por defecto de una aplicación ASP.NET MVC tiene 3 directorios:

  • /Controllers
  • /Models
  • /Views

Como podréis 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 patrón y os lo recomendamos ya que es una forma fácil de estructurar la aplicación. A menos que tengáis una buena razón para usar una estructura diferente, os recomiendo que useis este.

Mapeando URLs a clases de controladores.

En la mayoría 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 petición http para ana aplicación web llega al servidor web, el web framework ejecuta el código de dicho archivo del disco, y este código asume la administración de la petición. Normalmente ese código 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 interacción del usuario, y ejecutan la lógica necesaria basándose 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 cómo 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. También 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 más 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 están 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 código usando un conjunto de convenciones para nombrar las urls que van a usarse en nuestra aplicación en ASP.NET en el archivo Global.asax que se crea.

El convenio por defecto es mapear el path de la url de una petición HTTP (por ejemplo: /Products/) a una clase cuyo nombre siga el patrón UrlPathController (por ejemplo: una url que termine con /Products/ se mapeará a la clase ProductsController).

Para crear la funcionalidad de navegación de productos en nuestro ejemplo, añadimos 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 métodos auxiliares y funcionalidad que podría hacernos falta para aprovecharla más 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 cómo añadir 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 razón por la que nuestra aplicación usa este convenio por defecto es porque hay una regla de rutado que configura esta opción que se añade automáticamenet 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 queréis personalizarla, sólo tenéis que iros a la clase de la aplicación (en global.asax) y cambiarlo. Veremos cómo hacer esto en otro post (cuando veamos algunos escenarios interesantes que nos permite el rutado de URLs).

Comprendiendo los métodos de acción de los controladores.

Ahora que tenemos la clase ProductsController podemos empezar a añadir lógica 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 categorías de Productos, 2) Listar los productos de una categoría, y 3) Mostrar los detalles de un producto. Vamos a usar las siguientes urls para estos escenarios:

URL Format Behavior URL Example
/Products/Categories Browse all Product Categories /Products/Categories
/Products/List/Category List Products within a Category /Products/List/Beverages
/Products/Detail/ProductID Show Details about a Specific Product /Products/Detail/34

Hay un par de formas de escribir el código en nuestra clase ProductsController para procesar estos tres tipos de urls. Una forma podría ser "Ejecutar" un método en la clase base Controller y escribir nuestra lógica if/else/switch para ver qué url se ha solicitado, y entonces ejecutar la logica necesaria en cada caso.

Una forma más sencilla es usar una característica del framework MVC que nos permite definir "métodos de acción" en nuestro controlador, y que la clase base Controller invoque el método apropiado.

Por ejemplo, podemos añadir los siguientes métodos de acción a nuestra clase ProductsController :

Las reglas de rutado de urls que están 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 acción de la petición. Así que cuando recibimos una petición de la forma "/Products/Categories", la regla tratará la parte "Categories" como el nombre de la acción, y se invocará el método Categories() para procesar la petición. Si recivimos una petición a la url /Products/Detail/5, la regla cojerá el nombre "Detail" como el nombre de la acción y se invocará al método Detail(), etc.

Nota: ASP.NET MVC no obliga a usar este convenio de nombres. Si queremos usar otro patrón de mapeado para las urls, podemos cambiarlo en la clase de la aplicación de ASP.NET (en Global.asax) y cambiarlo.

Mapeando parámetros URL a métodos de acción

Hay varias formas de acceder a los parámetros de las urls en los métodos de acción 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 están 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 más profundidad en futuros post.

Aquí tenéis un ejemplo de cómo usar el API Request para obtener un ID querystring desde la acción Detail de nuestra clase ProductsController:

ASP.NET MVC también nos permite mapear automáticamente parámetros de la url como parámetros de métodos de acción. Por defecto, si tenemos un parámetro en algún método de acción, MVC mirará los datos de la petición para ver si hay algún valor que se corresponda con el mismo nombre. Si lo hay, lo pasará automáticamente como parámetro del método de acción.

Por ejemplo, podemos reescribir el método de acción Detail use esta característica:

Además de mapear los valores de los argumentos de la querystring de una petición, ASP.NET MVC tamibén 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 podríamos usar /Products/Detail/3).

La regla de mapeo por defecto cuando creamos un nuevo proyecto MVC es: "[controller]/[action]/[id]". Vemos que si hay algún sub-path en la dirección después del nombre del método de acción, lo tratará como un parámetro - y podremos pasarlo directamente al método de acción como argumento:

Podemos hacer lo mismo para la acción List (Listar) de manera que le pasamos el nombre de la categoría en la url (por ejemplo: /Products/List/Beverages). Para hacer el código más claro, he hecho un truco en las reglas de rutado en lugar de pasar un argumento con nombre "id" sea "category" para esta acción.

Aquí teneis una versión de nuestra clase ProductsController que ahora tiene un rutado url completo y un soporte para el mapeo de parámetros:

Fijáos cómo en la acción List coge el parámetro de la categoría como parte de la URL, y un índice de página opcional  (implementaremos paginado en el servidor y usaremos ese valor para indicar qué página de categoría mostrar en la petición).

Los parámetros opcionales se manejan usando tipos de datos nullables en los métodos de acción de los controladores. Debido a que el parámetro de nuestra acción 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 cómo funcionan los tipos nulables.

Construllendo objetos de nuestro modelo de datos.

Ya tenemos la clase ProductsController y tres métodos de acción 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 aplicación 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 patrón 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 también.

Para nuestra aplicación de ejemplo usaremos el ORM que viene con .NET 3.5 y VS 2008 llamado LINQ to SQL. Podéis aprender más sobre él en la serie de post que escribí (en concreto aquí tenéis los post: post1, post2, post3 y post4).

Empezaremos haciendo clic con el botón derecho en el directorio "Models" de nuestro proyecto MVC y elegimos "Add New Item" para añadir un modelo LINQ to SQL. En el diseñador 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, añadiremos una nueva clase parcial NorthwindDataContext al directorio Models:

En esta clase definiremos unos cuantos métodos que nos servirán para encapsular algunas expresiones LINQ que usaremos para obtener los objetos Category de nuestra base de datos, obtener todos los productos de una categoría y un producto concreto a partir del campo ProductID:

Estos métodos nos harán más facil y claro obtener los objetos del modelo de datos que necesitamos para nuestra clase ProductsController (sin tener que escribir ninguna expresión LINQ en nuestra clase controlador):

Ahora tenemos todo el código necesario para la funcionalidad de la clase ProductsController.

Terminando la implementación de la clase ProductsController

Los controladores de una aplicación basada en MVC son los responsables de procesar las peticiones entrantes, administrar las entradas de usuario y sus interacciones, y ejecutar la lógica 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 aplicación - que se implementan en clases a parte de los controladores. Las vistas se centrar en encapsular toda la lógica de presentación y no deben contener nada de la lógica de la aplicación o accesos a la base de datos (esto es tarea de los controladores).

En un workflow web MVC típico, los métodos de acción de los controladores administran las peticiones web, usan los parámetros para ejecutar la lógica 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 estaréis 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 motivación de particionar la aplicación de esta manera es para ayudar a la separación entre lógica de negocio y la generación de la interfaz de suuario. Esto hace mucho más sencillo el hacer test unitarios a nuestra logica de datos por una parte y para la presentación en otra. También puede ayudarnos a hacer que nuestra aplicación sea más mantenible con el paso del tiempo - hace más difícil que añadamos parte de la lógica en la capa de presentación.

Cuando implementamos los tres métodos de acción del controlador en la clase ProductsController, usaremos los parámetros 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 métodos RenderView() de la clase base Controller para espcificar la vista que queramos usar, así como explicitamente le pasaremos los datos específicos que queremos que la vista use para renderizar la respuesta.

Aquí tenéis el resultado final de la clase ProductsController:

Fíjáos que el número de líneas de codigo que tienen los métodos de acción es muy poco (dos líneas cada uno). Esto es debido a que la lógica de paso de parametros de las URLs es administrada completamente por el framework MVC (ahorrándonos el tener que escribir todo ese código). También es debido a que el escenario de navegación entre productos es muy simple desde el punto de vista de la lógica de negocio (los métodos de acción son sólo escenarios de lectura).

En general nos encontraremos con los llamados "skinny controllers" (controladores flacos) - haciendo referencia a aquellos controladores cuyos métodos de acción no tienen más de 10 líneas de código. Esto suele ser una señal de que hemos encapsulado muy bien la lógica de datos y factorizado la lógica de controlador bien.

Tests unitarios de la clase ProductsController

Os sorprenderá ver que el siguiente paso es trabajar en el testing de la lógica de la aplicación y la funcionalidad. Preguntaréis ¿cómo es posible? No hemos implementado nada en las vistas y nuestra aplicación a día 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 lógico de las aplicaciones completamente independiente de la generación 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 añadiremos una clase nueva ProductsControllerTester al proyecto de Test que se creó en la solución por defecto cuando creamos el proyecto en Visual Studio:

Ahora definiremos un test simple que comprueba la acción Detail:

El framework ASP.NET MVC ha sido diseñado especialmente para que sea fácil hacer test unitarios. Todas las APIS y contratos del framework son interfaces, y los puntos de extensión están disponibles para permitir una injección y personalización fácil de objetos (incluyendo la habilidad de usar contenedores IOC como Windsor, StructureMap, Spring.NET y ObjectBuilder). Los desarrolladores serán 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 cómo inyectar una implementacion tonta de "ViewFactory" en nuestra clase ProductsController antes de llamar al método de acción Detail(). De esta forma estamos sobreescribiendo la factoría de vistas por defecto que se usaría para renderizar nuestra vista. Podemos usar esta implementación de factoría de vistas para aislar el testeado del comportamiento del método Detail de ProductsController (y no involucrar ninguna vista para hacer esto). Fijaos cómo podemos usar las tres sentencias Assert después del método Detail() para verificar el correcto comportaminto d eéste (específicamente que la acción 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 librería y testearla directamente. Esto puede acelerar la velocidad de ejecución de test unitarios, y simplificar la configuración y la ejecución 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 daréis 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 aplicación y la lógica de datos para la sección de navegación entre productos de nuestra aplicación d e-comerce. Ahora tenemos que implementar una interfaz de usuario HTML para ella.

Lo haremos implementando unas "Vistas" que renderizarán la interfaz de usuario adecuada usando objetos de datos relacionados con las vistas que los métodos de acción de la clase ProductsController proporcionarán cuando se llame al método RenderView():

En este código vemos que el parámetro "Categories" está indicando el nombre de la vista que queremos renderizar, y el segundo parámetro 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 páginas ASP.NET (.aspx), Master Pages (.master) y UserControls (.ascx).

Usaermos el motor de ASP.NET para implementar la interfaz de usuario de nuestra aplicación.

Definiendo el archivo Site.Master

Como vamos a estar creando varias páginas para nuestro sitio, empezaremos definiendo una master page que usaremos para encapsular el HTML común 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 páginas rellenaran con su contenido propio. Podemos usar todas las características del diseñador 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 aplicación.

Cuando creamos vistas que son específicas para un sólo controlador, la convención 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 método 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 Categorías

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 página 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 código de las vistas lo más simple posible, y asegurarnos de que ese código sólo se encarga de renderizar la interfaz de usuario. La lógica de la aplicación y de los datos sólo debería estar escrito en las clases controlador. Estas clases pueden elegir pasar los objetos necesarios para renderizar una vista cuando llamen al método RenderView. Por ejemplo, aquí teneís el método de acción de Categories de nuestra clase ProductsController que le pasa una colección List de objetos categoría:

Las páginas de vistas de MVC derivan de System.Web.Mvc.ViewPage que nos proporciona un conjunto de métodos de ayuda y propiedades que podemos usar para la generación de la interfaz de usuario. Una de esas propiedades es "ViewData", y nos da acceso a objetos de datos específicos de la vista que el controlador le pasa como argumentos al método 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 genéricos 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 código trasero de nuestra vista de Categorías 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 compilación en el código 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 categoría en la vista de Categorías:

Podemos escribir esta generación de la interfaz de usuario de dos formas: 1) Usando código inline en el .aspx o 2) Usando controles de servidor en el .aspx y enlazarlos desde el código trasero.

Primera opcion: Código inline

Las páginas ASP.NET, los User Controls y las Master Pages soportan la sintaxis de <% %> y <%= &> para insertar código en el rendering del html. Podemos usar esta técnica con nuestra vista de categorías para escribir un bucle foreach que genere la lista en html:

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

También nos permite debugear el código inline (permitiéndonos poner puntos de ruptura):

Segunda opción: Usando controles de servidor

Las páginas ASP.NET, los User Controls y las Master Pages también tienen la habilidad de usar contorles de servidor declarativos para encapsular la generación del HTML. En lugar de usar el código inline, podemos usar el nuevo control <asp:listview> de .NET 3.5 para generar la lista:

Fijaos cómo 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 categoría al control listview en el código trasero:

Importante: En el mundo MVC sólo querremos poner lógica de renderizado en el código trasero de las vistas (y ninguna lógica de la aplicación). Arriba, la única lógica que tenemos es la asignación de la colección fuertemente tipada de ViewData al control ListView. El controlador ProductsController es el único que obtiene la lista de categorías de la base de datos, no la vista.

El control ListView de la vista generará el mismo HTML que el código inline de la primera opción. Como no queremos tener un control <form runat="server"> en la página, sin viewstate, valores ID, no se emitirá nada. Sólo CSS y HTML:

El método Html.ActionLink

Una de las cosas de las que os habréis dado cuenta tanto en la opción del código inline como en la versión con un control de servidor son las llamadas al método Html.ActionLink:

El objeto Html es una propiedad de ayuda en la clase base ViewPage, y el método ActionLink es una forma de hacer sencillo el generar enlaces HTML que enlacen a los métodos de acción de los controladores. Si miramos la salida HTML podemos ver un ejemplo del HTML generado por este método:

 <a href="http://weblogs.asp.net/Products/List/Beverages" mce_href="http://weblogs.asp.net/Products/List/Beverages">Beverages</a>

Esta firma del método Html.ActionLink que estamos usando es de esta forma:

string ActionLink(string text, object values);

El primer parámtero representa al contenido del enlace a renderizar (por ejemplo: <a> el texto que va aquí </a>. El segundo argumento es un tipo anónimo 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 más 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 código en la vista de categorías del ProductController:

<%= Html.ActionLink("Click Me to See Beverages", new { action="List", category="Beverages" } %>

El método ActionLink usará las reglas de mapeo de url de nuestra aplicación para enmascararla en los parámetros 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 fácil a nuestra aplicación generar URLs y callbacks de AJAX en nuestros Controladores. También implica el poder actualizar las reglas de rutado de URLs en un lugar y tener el código disponible en toda nuestra aplicación.

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) sólo para renderizar la salida. Esto asegura que la lógica de Vistas sigue siendo mínima y sólo se centra en la renderización, pudiendo así testear las clases controlador y comprobar toda la lógica de datos y de la aplicación 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 cómo podemos crear un escenario real típico. Los primeros bits de ASP.NET MVC estará disponible en unas pocas semanas, y podréis usarlo para hacer todos los pasos de arriba.

Aunque la mayoría de los conceptos de MVC (en particular la idea de separar conceptos) sean nuevos a muchos lectores, este post también ha mostrado que la implementación 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. Características no relacionadas con la interfaz de usuario de ASP.NET como Autenticación 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 estarán soportados y extendidos. Incluso podéis crear una aplicación y tener partes escritas con WebForms y partes con MVC si queréis.

Si os ha gustado lo que habéis visto de MVC (o si queréis aprender más), estad atentos al blog en las próximas semanas. Escribiré sobre más conceptos de MVC y los usaremos para crear nuestra aplicación de e-commerce para ver más características.

Espero que sirva

Scott.

Traducido por: Juan María Laó Ramos. Microsoft Student Partner.

Artículo original: http://weblogs.asp.net/scottgu/archive/2007/11/13/asp-net-mvc-framework-part-1.aspx?CommentPosted=true#commentmessage

39 pensamientos en “ASP.NET MVC Framework (Primera parte)

  1. Pingback: ASP.NET MVC Framework (3ª Parte): Pasando ViewData desde Controladores a las Vistas « Thinking in .NET

  2. Pingback: ASP.NET MVC Framework (4ª Parte): Escenarios de edición y publicación « Thinking in .NET

    1. walter cordero

      si mi chavo Spring es buenísimo, pero acordate que habemos muchos que java no nos gusta para nada, o quienes en el trabajo solo usamos productos Microsoft.

  3. Vio

    Hola AB:

    Por lo que tengo entendido Spring es una metodología de desarrollo ágil. Basandose en establecer mini-hitos a cumplir en el proceso de desarrollo, teniendo como objetivo principal el no perder el control del desarrollo y saber en qué estado se encuentra el proyecto en cualquier momento. ¿Me estoy equivocando mucho?
    ASP.NET MVC Framework es una tecnología para crear aplicaciones web siguiendo el patron Modelo Vista-Controlador, como dices es practicamente igual que Faces de java. Pero con ésta sólo puedes hacer cosas con java, desarrollo con java. Y con ASP.NET MVC puedes hacer lo mismo que con faces en aplicaciones web con cualquier lenguaje de .NET, esto es: J#, C#, VB, IronPython, etc, etc.

  4. Adolfo

    Vio, creo que estas confundiendo Spring con Sprint (este si es un concepto de Scrum)

    Por cierto, de Spring, existe una versión implementada en .NET.

    AB, no te falta razón, tal vez estemos redescrubriendo la rueda, esta no es más que una de las implementaciones del modelo que ya existen en .Net, la única diferencia que le encuentro es el soporte de Microsoft. Personalmente me gusta esta implementación por que supongo que será bastante educativa, y tendra una mayor adopción que las anterior, pero bueno, no soy futurologo.

  5. Adolfo

    Vio, creo que estas confundiendo Spring con Sprint (este si es un concepto de Scrum)

    Por cierto, de Spring, existe una versión implementada en .NET.

    AB, no te falta razón, tal vez estemos redescrubriendo la rueda, esta no es más que una de las implementaciones del modelo que ya existen en .Net, la única diferencia que le encuentro es el soporte de Microsoft. Personalmente me gusta esta implementación por que supongo que será bastante educativa, y tendra una mayor adopción que las anterior, pero bueno, no soy futurologo.

    Por cierto Vio, gracias por la traducción.

  6. Vio

    Gracias Adolfo por la corrección.
    Veo que Spring es más ambicioso ya que MVC está pensado para la web, y spring.net no.
    Estas cosas me pasan por culpa de las palabras, que se diferencian sólo en una letra.
    Gracias de todas formas porque si no, no me habría enterado de que es Spring.

    Muchas gracias.

  7. victor

    baje el framework lo instale todo corre correctamente, pero al momento de subir el sitio en el server la sligas hacia las diferentes paginas no funcvionna me marca error como ke no encuentra al ruta, entonces supuse que hice algo mal, pero hice un proyecto mvc default el que vien como plantilla y lo subo al server y pasa lo mismo, y en le server ya instale el framework 3.5 las extensiones por favor alguien que me pueda ayudar

  8. Joan Valls

    Hola,

    Tengo instalado el VS 2008 Team System Architecture Edition, al que he añadido el ASP.NET MVC Preview 2, pero no consigo hacer un ASP.NET MVC Web Application ni a la de tres. De hecho, cuando voy a Nuevo Proyecto y escojo Web, no me aparece esta opción. ¿no se habrá instalado la plantilla? ¿A qué se puede deber? Desde luego, el VS 2008 es original, aunque me tiene perplejo que en todas las ayudas se hable de VS 2008 Professional y no del Architecture Edition. Si este que tengo yo cuesta 5 veces más, ¿cómo es posible que no contenga esta opción?

    Gracias de antemano por tu ayuda

    Joan Valls

  9. Pingback: Combinando ASP.NET MVC framework y jQuery, paso a paso - Variable not found en Geeks.ms

  10. Hernan

    Hola, efectivamente se esta redescubriendo la rueda. Microsoft quiso aportar su granito de arena con este patron. Pero hay aplicaciones como Castle Project Mono Rail que son para usar el patron MVC en ASP.NET.
    Saludos.
    Hernan

  11. Vio

    Tiene buena pinta Hernan.
    Pero parece tener los mismos problemas de la primera preview de ASP.NET MVC, a la hora de hacer test unitarios por ejemplo, los tipos pasados entre vistas y contrloadores no se pueden comprobar de una forma simple e intuitiva.
    Se llevan bien con ASP.NET AJAX?
    ¿Y funciona con .NET 3.5?

    Muchas gracias por tu comentario.

  12. Hernan

    Hola, te comento, en cuanto a patron MVC, yo prefiero usar MonoRail http://www.castleproject.org/MonoRail/
    La ventaja principal es que tiene mas antiguedad por lo tanto esta mas probado y tambien esta hecho exclusivamente para .NET.
    La ventaja principal es que funciona con el Framework 2.0 (y supongo que con el 1.1 tambien), cosa que no he podido hacer con ASP.NET MVC.
    Si alguien sabe como correr MVC en framework 2.0, me seria util.

    Respecto a ASP.NET AJax, sinceramente casi no lo uso, ya que genera demasiados script para algo muy simple. Costo-Beneficio.
    Si queres incursionar en Ajax, recomiendo Jayrock, un framework diseñado para convertir de C# a Json.
    Saludos.

  13. Idilio Gainza

    En estos momentos empiezo a investigar sobre la programacion para aplicacines wueb, estoy interesado en aprender ASP.NET, por lo que busco framework que me faciliten el trabajo, es decir que yo pueda diseñar mi aplicación y un avez definido el diseño pueda generar codigo.

    Alguien me puede ayudar o recomendar algo

  14. Vio

    Hola Idilio:

    Lo que estás buscando es Visual Studio, es de pago, pero tiene una versión gratuita un poco limitada. Busca el Visual Studio Express Edition hay uno especial para desarrollo web gratuito con ASP.NET.

    Espero que sirva

  15. Juan Pedro

    Buenas

    He intentado hacer este ejemplo pero no me tira. Algui que lo haya echo me lo puede pasar por la direccion de correo

    Muchas gracias

    Chao

  16. Yahaira

    Estoy de acuerdo con Vio…

    Oye no podrias pasarme el mismo ejemplo pero en visual basic, te lo agradeceria

  17. Juan

    excelente, si pudieras mandarme material adicional te lo agradecería infinitamente.

  18. Beto

    Hola, me bajé el VS2008 trial de la página de Microsoft, pero no veo que tenga la opcion de crear un proyecto ASP MVC. Hay que instalar algo más aparte de eso? O en esa versión no es posible desarrollar ese tipo de aplicaciones.

    Saludos

    1. Tango Feroz

      Beto, para poder utilizar el ASP.net MVC necesitas descargar el instalador de la pagina oficial http://www.asp.net y luego seguir los pasos de instalación. De hecho este framework no viene incluido en ninguna de las versiones 2008 del visual studio

  19. Daniel

    Muy buena explicación, recien estoy tomando conocimiento de esta tecnología y me parece interesante. Voy a seguir aprendiendo mas espero que escribas mas post sobre este tema.

  20. Pingback: Algunos conceptos de .NET « Civilis blog

  21. afm

    Muy bueno, lastima que las imagenes no se ven completas, asi que me quede a medias, pero al menos me ha dado una idea general.
    Gracias..

  22. Miki

    Para el que no haya caído en el tema de mas imágenes, pulsando el boton derecho del ratón en la imagen y después en “Ver Imagen” o “Abrir Imagen en una pestaña nueva”, se pueden ver 😉

  23. Mauro

    Dos cosas:
    1-Muy interesante. Lo voy a probar. Generalmente uso MVC en PHP con Joomla pero no en .NET . Creí que nunca lo incorporarían. Lo voy a probar.
    2-Las imágenes están completas. Solo desactiven la hoja de estilo del sitio y prodrán verlas completa.

  24. Juan Carlos Reátegui

    Estoy desarrollando un Sistema Completo para Costos de una Heladería (Heladería Guayos) de frutas tropicales exoticas de la Selva.
    ASP.Net esta buenazo como los Helados de aguaje y camucamu de Guayos.

    Provecho con MVC.

  25. Diego

    Sinceramente, me sorprende el entusiasmo con que mucha gente ve esta “Novedad” del patrón MVP aplicado a la WEB.
    Fundamentalmente porque para aplicar el patrón no es necesario este bendito Framework ni ningún otro invento (spring, sprint, jaja)
    Si hacemos un balance entre costo – beneficio – mantenibilidad y rapidez de desarrollo, menos entiendo a quienes fomentan esta forma de trabajar en la Web, pero lo respeto por supuesto.
    El MVC es aplicable desde finales del 70, y puedo aplicarlo tanto en aplicación de Consola, WinForm o WebForm, porque es un tema de “DISEÑO”, de “ARQUITECTURA” y no te tecnología.
    Es decir, quien haya estudiado (no por rendir un exámen, sino de verdad) sabe que este patrón, junto con el resto de los 23 patrones de GOF son la clave de un buen diseño, independientemente del tipo de aplicación.

    Bueno, finalmente mi consejo para aquellos que no saben si invertir tiempo en aprender todas estas cosas, es el siguiente:

  26. Diego

    Sinceramente, me sorprende el entusiasmo con que mucha gente ve esta “Novedad” del patrón MVP aplicado a la WEB.
    Fundamentalmente porque para aplicar el patrón no es necesario este bendito Framework ni ningún otro invento (spring, sprint, jaja)

    Si hacemos un balance entre costo – beneficio – mantenibilidad y rapidez de desarrollo, menos entiendo a quienes fomentan esta forma de trabajar en la Web, pero lo respeto por supuesto.

    El MVC es aplicable desde finales del 70, y puedo aplicarlo tanto en aplicación de Consola, WinForm o WebForm, porque es un tema de “DISEÑO”, de “ARQUITECTURA” y no te tecnología. Puedo estar aplicando perfectamente este patrón en WebForm sin necesidad de aprender todas estas cosas, solo hay que conocer mejor el patrón, su finalidad, y cómo combinarnlo con la tecnología existente.

    Es decir, quien haya estudiado (no por rendir un exámen, sino de verdad) sabe que este patrón, junto con el resto de los 23 patrones de GOF son la clave de un buen diseño, independientemente del tipo de aplicación.

    Bueno, finalmente mi consejo para aquellos que no saben si invertir tiempo en aprender todas estas cosas, es el siguiente: estudiá, hay mucho material de diseño y patrones dando vueltas. No compres una moda o tendencia.

    Pd: mi intención es simplemente mostrar una visión diferente, no subestimar a quienes ya estan embarcados en esta tecnología.

  27. Juanma

    Hola Diego:

    Gracias por tus comentarios.
    Como sabes, ASP.NET MVC es un framework más para ASP.NET. Para ayudar en según qué casos reducir el costo, aumentar el beneficio, la mantenimibilidad y rapidez de desarrollo manteniendo en equilibrio inestable (como me gusta decir) la relación entre acoplamiento-cohesión-complejidad .
    Esa es la base por la que salen todos los patrones de diseño, arquitecturales o no.
    ASP.NET MVC es un ejercicio de abstracción más en el desarrollo, como todos los frameworks (Spring, Hibernate, .NET, Java, ect).

    Un saludo.

    1. Francisco

      Muy interesantes todas las publicaciones.
      Tengo una duda en la que quiza me puedan ayudar.

      Me encuentro desarrollando un sistema web y estoy en la parte del diseño arquitectonico que es muy importante para definir la estructura general del mismo.

      Me parece que el patron MVC es el mas adecuado para este tipo de aplicaciones, sin embargo estoy en duda sobre la plataforma a utilizar ( java, o .net).

      Por otro lado quiero incluir también conceptos de web 2.0 y he estado viendo frameworks con modulos ya desarrollados que me pueden servir como drupal o joomla.

      Quisiera saber qué tan bueno sería incluir frameworks como joomla y como afectaría mi arquitectura ( joomla está hecho en php) y si además puedo utilizar partes de joomla con partes de .net ( ya que .net tiene una curva de aprendizaje más corta) y como es que afecta a la arquitectura usando MVC.

      Espero me puedan ayudar.

      Muchas Gracias de antemano.
      Saludos,
      Francisco

  28. Junior

    hola estoy siguiendo tu guía y tengo una duda, cuando haces el modelo LINQ tu base de datos tiene métodos o procedures ?
    Estoy siguiendo tu guia en el visual studio 2010 y te cuento que si lo sigo al pie de la letra me aparece un error en la linea:
    >return Products.where
    Error que me arroja dice que no contiene definicion para where, igual para ToList().

    Y otro detalle [ControllerAction] es obligatorio ? cuando yo lo agrego me arroja error.

    Bueno espero que respondas si aun estas vigente en tu blog. Gracias.

  29. Lorena

    Hola!

    Espero me puedan ayudar deseo ejecutar mi aplicacion web asp.net mvc 2 en un servidor IIS pero no sé como hacerlo… Si pudieran ayudarme con un tutorial o algun link en donde pueda descargar la informacion. Gracias y espero puedan ayudarme =D

  30. jose yamberla

    El artículo es muy bueno. Lo que yo les suguiero es que sean más explícitos, ya que hay gente que no sabe casi nada de esto y necesita aprender….

Los comentarios están cerrados.