lunes, 22 de marzo de 2010
Una interesante característica de ASP.NET MVC, o más concretamente del conjunto de herramientas incluidas en Visual Studio para darle soporte, es la posibilidad de personalizar las plantillas que el IDE utiliza a la hora de agregar controladores y vistas a un proyecto.
Por ejemplo, cuando añadimos un controlador a nuestro proyecto, el entorno genera por defecto un código como el mostrado a continuación:
Pero, ¿qué ocurre si todos nuestros controladores heredan de un controlador base? ¿O si solemos incluir un código de inicialización o métodos comunes? ¿Y si disponemos de un código estandarizado para los métodos
Y lo mismo ocurre, aunque de forma aún más frecuente, con las vistas. Al añadir un elemento de este tipo al proyecto es posible seleccionar la plantilla a utilizar, que determinará el contenido generado automáticamente por Visual Studio en función de si se trata de una vista parcial o completa, si será fuertemente tipada y otros aspectos.
Las plantillas disponibles para las vistas aparecen en el desplegable “View Content” del cuadro de diálogo de creación, como se aprecia en la captura de pantalla adjunta.
El contenido generado en cada caso pocas veces resulta válido directamente y requiere bastantes retoques, que debemos repetir una y otra vez en todas las vistas que generemos. Incluso en bastantes ocasiones optaremos por seleccionar la plantilla en blanco (“empty” en el desplegable) para introducir nuestro propio código.
Afortunadamente, podemos personalizar muy fácilmente las plantillas a utilizar en ambos casos:
Por ejemplo, en mi Visual Studio 2010 las podemos encontrar en C:\Archivos de programa\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates\AddController. En VS2008 es la misma, salvo por la versión del entorno, la 9.0.
En el interior de dicho directorio hay dos carpetas, llamadas “
En el caso del controlador, el archivo
Esto ya nos sugiere dos posibilidades a la hora de personalizar los contenidos de las vistas:
El código de las plantillas por defecto para las vistas es más complejo que el de los controladores, pero aun sin conocimientos de T4 pueden ser adaptados con relativa facilidad.
En el caso de la creación de controladores, si existe ~
Para las vistas, Visual Studio poblará el desplegable de plantillas de contenido a partir de los archivos .tt disponibles en la carpeta
Eso sí, para que el compilador no genere errores al ejecutar las plantillas a deshora, es necesario retocar sus propiedades, eliminando la herramienta personalizada y dejando esta propiedad en blanco.
Publicado en: Variable not found.
Hey, ¡estoy en Twitter!
Por ejemplo, cuando añadimos un controlador a nuestro proyecto, el entorno genera por defecto un código como el mostrado a continuación:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MiProyecto.Controllers
{
public class ProductsController : Controller
{
//
// GET: /Products/
public ActionResult Index()
{
return View();
}
}
}
Pero, ¿qué ocurre si todos nuestros controladores heredan de un controlador base? ¿O si solemos incluir un código de inicialización o métodos comunes? ¿Y si disponemos de un código estandarizado para los métodos
Create()
, Edit()
, etc? ¿O simplemente queremos cambiar el nombre de los métodos propuestos? En todos los casos la respuesta es simple: tendríamos que retocar a mano cada uno de los controladores para adaptarlos a nuestras necesidades.Y lo mismo ocurre, aunque de forma aún más frecuente, con las vistas. Al añadir un elemento de este tipo al proyecto es posible seleccionar la plantilla a utilizar, que determinará el contenido generado automáticamente por Visual Studio en función de si se trata de una vista parcial o completa, si será fuertemente tipada y otros aspectos.
Las plantillas disponibles para las vistas aparecen en el desplegable “View Content” del cuadro de diálogo de creación, como se aprecia en la captura de pantalla adjunta.
El contenido generado en cada caso pocas veces resulta válido directamente y requiere bastantes retoques, que debemos repetir una y otra vez en todas las vistas que generemos. Incluso en bastantes ocasiones optaremos por seleccionar la plantilla en blanco (“empty” en el desplegable) para introducir nuestro propio código.
Afortunadamente, podemos personalizar muy fácilmente las plantillas a utilizar en ambos casos:
- de forma global, es decir, a nivel de entorno de desarrollo en el equipo, por lo que las modificaciones que introduzcamos estarán disponibles para todos los proyectos MVC que utilicemos.
- de forma local al proyecto, siendo utilizadas únicamente al crear elementos dentro del mismo.
Personalización a nivel de equipo
Las plantillas que utiliza Visual Studio para generar tanto los controladores como las vistas están almacenadas en la carpetaCommon7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates
, dentro del directorio de instalación del IDE.Por ejemplo, en mi Visual Studio 2010 las podemos encontrar en C:\Archivos de programa\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates\AddController. En VS2008 es la misma, salvo por la versión del entorno, la 9.0.
En el interior de dicho directorio hay dos carpetas, llamadas “
AddController
” y “AddView
” en las que encontraremos las plantillas T4 que generan el código por defecto para controladores y vistas, respectivamente.En el caso del controlador, el archivo
AddController/Controller.tt
contiene la lógica de generación del código, a la que podemos acceder abriendo el archivo con un editor de texto plano, o con el propio Visual Studio, y realizar los cambios que estimemos oportunos (¡previo backup del archivo original, claro!). Aunque T4 puede llegar a ser complejo, en la plantilla de controladores el código resulta bastante fácil de leer, comprender y modificar.Para las vistas, si accedemos a la carpeta
AddView
, observaremos que existe una plantilla T4 para cada una de las posibilidades de contenido disponibles en el desplegable del formulario de diálogo de creación de este tipo de elementos:Esto ya nos sugiere dos posibilidades a la hora de personalizar los contenidos de las vistas:
- la primera, sustituir el contenido de las plantillas incluidas por defecto, como “Create”, o “Details”, de la misma forma que hemos hecho anteriormente con los controladores,
- o bien, crear nuestras propias plantillas. De hecho, cualquier archivo .tt añadido a esta carpeta hará que en el desplegable aparezca un nuevo elemento:
El código de las plantillas por defecto para las vistas es más complejo que el de los controladores, pero aun sin conocimientos de T4 pueden ser adaptados con relativa facilidad.
Personalización a nivel de proyecto
Una vez comprendido lo anterior, la personalización a nivel exclusivamente de proyecto es trivial, gracias a un interesante detalle: Visual Studio, a la hora de generar el código de controladores y vistas acude en primer lugar la carpeta~/CodeTemplates
del proyecto, y busca unas subcarpetas llamadas AddController
y AddView
, desde donde según el caso, tomará las plantillas.En el caso de la creación de controladores, si existe ~
/CodeTemplates/AddController/Controller.tt
, se utilizará como plantilla en lugar de la definida a nivel de equipo.Para las vistas, Visual Studio poblará el desplegable de plantillas de contenido a partir de los archivos .tt disponibles en la carpeta
~/CodeTemplates/AddView
, sumados a los existentes en la carpeta de la aplicación. En caso de coincidencia de nombre (por ejemplo, si creamos aquí una nueva plantilla “Create.tt”) se tomará siempre la plantilla definida en el proyecto.Eso sí, para que el compilador no genere errores al ejecutar las plantillas a deshora, es necesario retocar sus propiedades, eliminando la herramienta personalizada y dejando esta propiedad en blanco.
Publicado en: Variable not found.
Hey, ¡estoy en Twitter!
martes, 16 de marzo de 2010
Como ya he comentado extensamente por aquí, T4MVC es una herramienta imprescindible a la hora de evitar “cadenas mágicas” cuando desarrollamos sistemas con el framework MVC.
A pesar de sus innumerables virtudes, desde sus comienzos este proyecto arrastra una molesta y pesada carga, asociada a la forma en que Visual Studio utiliza las T4: en principio sólo se ejecutan, y generan código por lo tanto, cuando el archivo de la plantilla es modificado.
David Ebbo, el creado de T4MVC, aportó una solución transitoria al problema incluyendo en la propia plantilla el código para engañar al IDE, haciéndole creer que el archivo .tt estaba siendo modificado continuamente, y forzando así el proceso de generación de código. Aunque válida, resultaba un poco molesto tener el archivo siempre abierto para que este proceso funcionara.
Hace unos días, David comenta en su post “A better way to auto-run T4MVC when you build” la solución propuesta por Joachim Lykke Andersen y que describe en su entrada “How to run T4MVC on build”, consistente en la creación de una macro de Visual Studio capaz de tomar el control antes del inicio de la compilación, e invocar la herramienta personalizada de generación de código.
Y de hecho, tanto ha convencido esta nueva solución a David, el padre de la criatura, que está considerando seriamente la eliminación de la solución de mantener el archivo continuamente modificado en favor de ésta, mucho más limpia.
El procedimiento es el siguiente. En primer lugar, accedemos al entorno de desarrollo de macros, pulsando ALT y F11, y seleccionamos a continuación MyMacros > EnvironmentEvents.
Aparecerá un módulo VB sobre cuyo cuerpo pegamos la siguiente porción de código que, como se puede observar simplemente introduce una lógica al iniciar la compilación mediante la cual se localiza el archivo de plantilla “T4MVC.tt” y se invoca a su herramienta personalizada, en este caso, el generador de código T4:
Si aparece un error indicando que no existe la clase
A pesar de sus innumerables virtudes, desde sus comienzos este proyecto arrastra una molesta y pesada carga, asociada a la forma en que Visual Studio utiliza las T4: en principio sólo se ejecutan, y generan código por lo tanto, cuando el archivo de la plantilla es modificado.
David Ebbo, el creado de T4MVC, aportó una solución transitoria al problema incluyendo en la propia plantilla el código para engañar al IDE, haciéndole creer que el archivo .tt estaba siendo modificado continuamente, y forzando así el proceso de generación de código. Aunque válida, resultaba un poco molesto tener el archivo siempre abierto para que este proceso funcionara.
Hace unos días, David comenta en su post “A better way to auto-run T4MVC when you build” la solución propuesta por Joachim Lykke Andersen y que describe en su entrada “How to run T4MVC on build”, consistente en la creación de una macro de Visual Studio capaz de tomar el control antes del inicio de la compilación, e invocar la herramienta personalizada de generación de código.
Y de hecho, tanto ha convencido esta nueva solución a David, el padre de la criatura, que está considerando seriamente la eliminación de la solución de mantener el archivo continuamente modificado en favor de ésta, mucho más limpia.
El procedimiento es el siguiente. En primer lugar, accedemos al entorno de desarrollo de macros, pulsando ALT y F11, y seleccionamos a continuación MyMacros > EnvironmentEvents.
Aparecerá un módulo VB sobre cuyo cuerpo pegamos la siguiente porción de código que, como se puede observar simplemente introduce una lógica al iniciar la compilación mediante la cual se localiza el archivo de plantilla “T4MVC.tt” y se invoca a su herramienta personalizada, en este caso, el generador de código T4:
Public Sub OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, _
ByVal Action As EnvDTE.vsBuildAction) _
Handles BuildEvents.OnBuildBegin
If Scope = vsBuildScope.vsBuildScopeSolution _
Or Scope = vsBuildScope.vsBuildScopeProject Then
Dim projectItem As VSProjectItem _
= DTE.Solution.FindProjectItem("T4MVC.tt").Object
If Not projectItem Is Nothing Then
projectItem.RunCustomTool()
End If
End If
End Sub
Si aparece un error indicando que no existe la clase
VSProjectItem
, basta con incluir la referencia apropiada, utilizando la solución ofrecida por el propio entorno:Una vez finalizado, salvamos la macro y listo. A partir de ese momento, el generador será ejecutado en cada compilación de proyectos y soluciones.
De esta forma tenemos una solución que nos permite, una vez configurada la macro, olvidarnos del proceso de generación, delegándolo al entorno de desarrollo, que para eso está ;-). En breve, además, se espera la aparición de un plugin para Visual Studio que permita automatizar aún más el proceso, y la posibilidad de activar y desactivar este comportamiento; hay que tener en cuenta que al tratarse de una macro a nivel de entorno, se ejecutará siempre al compilar cualquier tipo de proyecto, sea MVC o no.
En definitiva, es una prueba más de que a veces los problemas tienen soluciones mucho más simples de lo que en principio puede parecer. Y por cierto, la idea es aplicable a cualquier tipo de plantilla T4, simplemente retocándolo un poco ;-)
Editado 17/03/2010: ha aparecido un plugin para VS2008 que realiza esta misma tarea.
Publicado en: Variable not found
Hey, ¡estoy en twitter!
domingo, 14 de marzo de 2010
Estando todavía calentito el horno del que acaba de salir ASP.NET MVC 2, es curioso conocer algo sobre el próximo plato que están preparando Haack y su equipo: ¡ASP.NET MVC 3!
Ya se han publicado algunas de las líneas y objetivos que guiarán los futuros desarrollos y determinados detalles que probablemente serán incluidos, aunque obviamente todavía pueden variar sustancialmente. Las principales áreas de atención son en estos momentos:
Asimismo, se dará soporte a nuevos atributos de validación además de los ya incluidos en las anotaciones de datos de ASP.NET 4.
Otra línea en estudio es la introducción de herramientas basadas en línea de comandos, alternativas a las incluidas en Visual Studio.
Para facilitar el uso de Ajax se introducirán nuevos helpers que simplifiquen la implementación de escenarios habituales, como los controles de selección de fecha o cuadros de edición con auto-completado.
También se considera interesante ampliar los helpers existentes para permitir la actualización parcial de varias zonas de la página, e incluir soporte para plantillas en cliente, que permitan añadir el marcado muy rápidamente a datos retornados en formato JSON desde el controlador.
Desde el punto de vista arquitectural, se planea seguir flexibilizando el framework, permitiendo nuevos puntos de inyección de dependencias, como la instanciación de clases del Modelo desde el binder, o en filtros de acción.
Se prevé también la creación de una factoría de controladores para MEF (Managed Extensibility Framework) que permitan ampliar las funcionalidades de un sistema mediante sus sencillos mecanismos de extensibilidad, sin necesidad de recompilar.
Se incrementarán también las herramientas de generación de andamiaje de aplicaciones, para acelerar la implementación de escenarios comunes, como los habituales CRUD.
Y respecto al rendimiento, el equipo está estudiando nuevas técnicas para mejorar el cacheo de respuestas, y soluciones como aumentar el control sobre el estado de sesión, permitiendo activarlo o desactivarlo para mejorar la eficiencia.
En cualquier caso, lo que sí ha dejado claro Phil es que ASP.NET MVC 2 será la última versión del framework con soporte para ASP.NET 3.5 SP1.
Fuente: Roadmap de ASP.NET MVC.
Publicado en: Variable not found.
Hey, ¡estoy en twitter!
Ya se han publicado algunas de las líneas y objetivos que guiarán los futuros desarrollos y determinados detalles que probablemente serán incluidos, aunque obviamente todavía pueden variar sustancialmente. Las principales áreas de atención son en estos momentos:
- aumentar la productividad de los desarrolladores,
- facilitar aún más el uso de Ajax,
- incluir mejoras arquitecturales,
- y aumentar el rendimiento.
Asimismo, se dará soporte a nuevos atributos de validación además de los ya incluidos en las anotaciones de datos de ASP.NET 4.
Otra línea en estudio es la introducción de herramientas basadas en línea de comandos, alternativas a las incluidas en Visual Studio.
Para facilitar el uso de Ajax se introducirán nuevos helpers que simplifiquen la implementación de escenarios habituales, como los controles de selección de fecha o cuadros de edición con auto-completado.
También se considera interesante ampliar los helpers existentes para permitir la actualización parcial de varias zonas de la página, e incluir soporte para plantillas en cliente, que permitan añadir el marcado muy rápidamente a datos retornados en formato JSON desde el controlador.
Desde el punto de vista arquitectural, se planea seguir flexibilizando el framework, permitiendo nuevos puntos de inyección de dependencias, como la instanciación de clases del Modelo desde el binder, o en filtros de acción.
Se prevé también la creación de una factoría de controladores para MEF (Managed Extensibility Framework) que permitan ampliar las funcionalidades de un sistema mediante sus sencillos mecanismos de extensibilidad, sin necesidad de recompilar.
Se incrementarán también las herramientas de generación de andamiaje de aplicaciones, para acelerar la implementación de escenarios comunes, como los habituales CRUD.
Y respecto al rendimiento, el equipo está estudiando nuevas técnicas para mejorar el cacheo de respuestas, y soluciones como aumentar el control sobre el estado de sesión, permitiendo activarlo o desactivarlo para mejorar la eficiencia.
En cualquier caso, lo que sí ha dejado claro Phil es que ASP.NET MVC 2 será la última versión del framework con soporte para ASP.NET 3.5 SP1.
Fuente: Roadmap de ASP.NET MVC.
Publicado en: Variable not found.
Hey, ¡estoy en twitter!
jueves, 11 de marzo de 2010
Hace unos minutos en Twitter se propagaba la noticia de la disponibilidad de la versión final de ASP.NET MVC 2, que puedes descargar ya desde el sitio web de Microsoft.
Según se indica en el documento de notas de la revisión, y como era previsible, no se puede instalar en equipos con Visual Studio 2010 RC. Por lo demás, no se ha introducido ningún cambio destacable desde la anterior revisión, la segunda Release Candidate.
También se ha publicado el código fuente en CodePlex.
Publicado en: Variable not found.
Según se indica en el documento de notas de la revisión, y como era previsible, no se puede instalar en equipos con Visual Studio 2010 RC. Por lo demás, no se ha introducido ningún cambio destacable desde la anterior revisión, la segunda Release Candidate.
También se ha publicado el código fuente en CodePlex.
[Actualizado 12/03] Cómo instalar el framework en máquinas con Visual Studio 2010 RC.
Publicado en: Variable not found.
martes, 9 de marzo de 2010
El framework ASP.NET MVC 2 ha introducido un nuevo filtro llamado
Supongamos el siguiente código en el controlador, digamos,
Atendiendo a la ruta por defecto, una petición del tipo
¿Y qué significa eso en la práctica? Pues básicamente que la acción sólo puede ser invocada desde una vista utilizando los métodos
Ambos métodos, aunque formaban parte del ensamblado futures desde hace algún tiempo, han sido por fin incluídos en MVC 2, y están destinados a introducir en la vista actual el resultado de la ejecución de una “acción hija”, por ejemplo así:
La diferencia entre ambos es que
Publicado en: Variable not found.
Hey, ¡estoy en twitter!
ChildActionOnly
que, como su nombre indica, impide la ejecución del método de acción sobre el que se aplica, a menos que se trate de una “acción hija”. Supongamos el siguiente código en el controlador, digamos,
HomeController
:[ChildActionOnly]
public ActionResult Menu()
{
Menu mnu = getMenuForThisUser();
return PartialView(mnu);
}
Atendiendo a la ruta por defecto, una petición del tipo
GET /Home/Menu
generará una excepción, como la mostrada en la captura de pantalla adjunta.¿Y qué significa eso en la práctica? Pues básicamente que la acción sólo puede ser invocada desde una vista utilizando los métodos
Html.Action()
y Html.RenderAction()
.Ambos métodos, aunque formaban parte del ensamblado futures desde hace algún tiempo, han sido por fin incluídos en MVC 2, y están destinados a introducir en la vista actual el resultado de la ejecución de una “acción hija”, por ejemplo así:
<div id="mainMenu">
<%= Html.Action("Menu") %>
</div>
La diferencia entre ambos es que
Html.Action
retorna un string
con el resultado de la ejecución de dicha acción, mientras que Html.RenderAction()
escribirá directamente la respuesta sobre el canal de salida (Response
).Publicado en: Variable not found.
Hey, ¡estoy en twitter!
domingo, 7 de marzo de 2010
Esta es una respuesta rápida a una cuestión de Fred C., que me llega vía formulario de contacto en Variable not found, sobre un problemilla que también sufrí en algunas ocasiones, y he pensado que posiblemente pueda interesarle a alguien más, así que ahí va.
El escenario es el siguiente: tenemos en una vista un código para generar un enlace hacia una acción, como el mostrado a continuación:
Al mostrarse la vista, ya en tiempo de ejecución, nos encontramos con que no se ha generado el enlace que pretendíamos, sino uno como el mostrado en la captura de pantalla adjunta, hacia la dirección
En primer lugar, utilizando la ruta por defecto, vemos nos está llevando hacia el controlador “Home”, ¿pero no le habíamos dicho que era “Productos”?
Y en segundo lugar, ¿dónde está nuestro parámetro id? ¿De dónde sale ese parámetro
La respuesta a este problema es bien sencilla aunque al principio puede provocarnos algún dolor de cabeza: estamos utilizando una sobrecarga incorrecta del método ActionLink().
Si observamos las distintas sobrecargas de este método, podremos comprobar que sólo una de ellas tiene una signatura compatible con la llamada que estamos utilizando:
Así, cuando en el código anterior estábamos pasando al método el nombre del controlador, en realidad lo que hacíamos era indicarle los parámetros de la llamada. Eso explica el parámetro Length=9 en la URL: dado que le enviamos un string de 9 caracteres, simplemente se trata de una serialización de sus propiedades.
Y, por tanto, los parámetros de la llamada que estábamos especificando, lo hacíamos como parte de los atributos HTML. De hecho, si analizamos el código fuente de la página generada, encontramos que el parámetro “id” ha sido introducido como un atributo HTML del enlace:
La forma de solucionarlo es bien fácil, sólo hay que utilizar la sobrecarga apropiada, como:
En fin, que se trata de un pequeño despiste a la hora de codificar, propiciado a veces por la gran cantidad de sobrecargas y la información, algo confusa, ofrecida por Intellisense que nos puede hacer perder unos minutos muy valiosos.
¡Gracias, Fred, por participar en Variable not found!
Por cierto, ¡estoy en Twitter!
El escenario es el siguiente: tenemos en una vista un código para generar un enlace hacia una acción, como el mostrado a continuación:
<%= Html.ActionLink("Acceso externo", // Texto del enlace
"Editar", // Acción
"Productos", // Controlador
new { id=Model.Id }) // Parámetros
%>
Al mostrarse la vista, ya en tiempo de ejecución, nos encontramos con que no se ha generado el enlace que pretendíamos, sino uno como el mostrado en la captura de pantalla adjunta, hacia la dirección
/Home/Editar?Length=9
.En primer lugar, utilizando la ruta por defecto, vemos nos está llevando hacia el controlador “Home”, ¿pero no le habíamos dicho que era “Productos”?
Y en segundo lugar, ¿dónde está nuestro parámetro id? ¿De dónde sale ese parámetro
Length
con el valor 9?La respuesta a este problema es bien sencilla aunque al principio puede provocarnos algún dolor de cabeza: estamos utilizando una sobrecarga incorrecta del método ActionLink().
Si observamos las distintas sobrecargas de este método, podremos comprobar que sólo una de ellas tiene una signatura compatible con la llamada que estamos utilizando:
public static string ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
object routeValues,
object htmlAttributes
)
Y, por tanto, los parámetros de la llamada que estábamos especificando, lo hacíamos como parte de los atributos HTML. De hecho, si analizamos el código fuente de la página generada, encontramos que el parámetro “id” ha sido introducido como un atributo HTML del enlace:
<a href="/Home/Editar?Length=9" id="8">Editar este producto</a>
La forma de solucionarlo es bien fácil, sólo hay que utilizar la sobrecarga apropiada, como:
<%= Html.ActionLink("Editar este producto",
"Editar", // Acción
new { // Parámetros
controller="Productos",
id=Model.Id
}
) %>
// O Bien:
<%= Html.ActionLink(
"Editar este producto",
"Editar", // Acción
"Productos", // Controlador
new { id=Model.Id }, // Parámetros
null // Atributos HTML
) %>
En fin, que se trata de un pequeño despiste a la hora de codificar, propiciado a veces por la gran cantidad de sobrecargas y la información, algo confusa, ofrecida por Intellisense que nos puede hacer perder unos minutos muy valiosos.
¡Gracias, Fred, por participar en Variable not found!
Por cierto, ¡estoy en Twitter!