Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

Posts Tagged ‘atributos’

Restaurar ViewData luego de hacer un Redirect en ASP.NET MVC

Posted by Gerardo Contijoch en mayo 9, 2009

Una de las consecuencias de hacer un redirect desde una acción en ASP.NET MVC es que se pierde toda la información que tenemos guardada en el ViewData así como también en el ModelState. Esto se debe a que esos dos diccionario se utilizan para pasarle información desde los controladores a las vistas y si hacemos un redirect, lógicamente no vamos a estar devolviendo ninguna vista, por lo que sus valores son descartados cuando comienza la ejecución de la acción a la que redirigimos. Para evitar esto, se pueden guardar estos valores en la propiedad TempData, la cual es persistida entre requests. Como esto se puede volver un poco tedioso y repetitivo (por no decir poco elegante), decidí crear un par de ActionFilters que se encarguen de persistir valores del ViewData y de recuperarlos cuando sea necesario. El siguiente código esta basado en este post, el cual contiene la solución para persistir el ModelState.

Les presento un ejemplo de como se utilizan estos atributos y a continuación el código de los mismos.

   1: public class TestController : Controller {
   2:
   3:     [ExportarViewDataItem("Nombre")]
   4:     public RedirectToRouteResult AccionConRedirect() {
   5:         // ...
   6:         ViewData["Nombre"] = "Gerardo";
   7:         // ...
   8:         return RedirectToAction("Index");
   9:     }
  10:
  11:     [ImportarViewDataItem("Nombre")]
  12:     public ViewResult Index() {
  13:         string nombre = ViewData["nombre"].ToString();
  14:         // ...
  15:         return View();
  16:     }
  17:
  18: }

La implementación actual de los atributos requiere que se especifique un key a exportar e importar, pero los atributos son muy fácilmente modificables para que exporte e importe el ViewData completo sin necesidad de especificar cada uno de los keys.

Aca esta el código:

   1: /// <summary>
   2: /// Clase base para los atributos que exportan e importan valores de ViewData.
   3: /// </summary>
   4: public abstract class ViewDataTransferAttribute : ActionFilterAttribute {
   5:
   6:     public string Key { get; set; }
   7:
   8:     protected ViewDataTransferAttribute(string key) {
   9:         if (string.IsNullOrEmpty(key)) {
  10:             throw new ArgumentException("El parámetro key no puede ser null o vacío.", "key");
  11:         }
  12:
  13:         Key = key;
  14:     }
  15:
  16:     /// <summary>
  17:     /// Key interna para referenciar al valor guardado.
  18:     /// </summary>
  19:     /// <remarks>Se usa una key intena para asegurarnos de que no hay conflictos de keys con valores ya existentes.</remarks>
  20:     protected string InternalKey { get { return "_viewDataItem_" + Key; } }
  21: }
  22:
  23: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
  24: public class ExportarViewDataItemAttribute : ViewDataTransferAttribute {
  25:
  26:     public ExportarViewDataItemAttribute(string key) : base(key){}
  27:
  28:     public override void OnActionExecuted(ActionExecutedContext filterContext) {
  29:
  30:         // Si no hacemos un redirect, entonces no tiene sentido respaldar los valores
  31:         if (filterContext.Result is RedirectResult || filterContext.Result is RedirectToRouteResult) {
  32:
  33:             var valor = filterContext.Controller.ViewData[Key];
  34:
  35:             if (valor != null) {
  36:                 var tempData = filterContext.Controller.TempData;
  37:                 if (tempData.ContainsKey(InternalKey)) {
  38:                     tempData.Remove(InternalKey);
  39:                 }
  40:                 tempData.Add(InternalKey, valor);
  41:             }
  42:         }
  43:
  44:         base.OnActionExecuted(filterContext);
  45:     }
  46: }
  47:
  48: [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
  49: public class ImportarViewDataItemAttribute : ViewDataTransferAttribute {
  50:
  51:     public ImportarViewDataItemAttribute(string key) : base(key) { }
  52:
  53:     public override void OnActionExecuting(ActionExecutingContext filterContext) {
  54:
  55:         var valor = filterContext.Controller.TempData[InternalKey];
  56:
  57:         if (valor != null) {
  58:             var viewData = filterContext.Controller.ViewData;
  59:             if (viewData.ContainsKey(Key)) {
  60:                 viewData.Remove(Key);
  61:             }
  62:             viewData.Add(Key, valor);
  63:             // Limpiamos TempData
  64:             filterContext.Controller.TempData.Remove(InternalKey);
  65:         }
  66:
  67:         base.OnActionExecuting(filterContext);
  68:     }
  69: }

Se puede bajar el proyecto con los atributos y los tests asociados desde aquí.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in ASP.NET MVC, Desarrollo Web | Etiquetado: , , , , | Leave a Comment »

Crear mocks de clases internas con Rhino Mocks

Posted by Gerardo Contijoch en abril 10, 2009

Hoy descubrí que no se pueden crear mocks de clases con visibilidad internal (o Friend en VB) con Rhino Mocks, lo cual me llamó mucho la atención, mas que nada porque no me había topado nunca antes con ese problema y soy de usar mucho clases internas. El error que se nos presenta cuando intentamos hacerlo es el siguiente:

Castle.DynamicProxy.Generators.GeneratorException: Type is not public, so a proxy cannot be generated. Type: <NOMBRE_TIPO_INTERNAL>

Resulta que cuando creamos mocks, en realidad lo que creamos son proxies a las clases que mockeamos, los cuales interceptan las llamadas a las mismas y simulan el comportamiento que nosotros deseemos. Estos proxies, aparentemente, son generados en tiempo de ejecución en un assembly que se llama DynamicProxyGenAssembly2 (hay que revisar el código de Castle.DynamicProxy.ModuleScope para encontralo), el cual no tiene acceso a los miembros internos del assembly nuestro y es por eso que obtenemos el error que mencioné arriba.

Para solucionar esto, simplemente hay que agregar el atributo InternalsVisibleToAttribute a nuestro assembly y especificar DynamicProxyGenAssembly2 como parámetro.

En realidad no conozco mucho sobre el funcionamiento interno de las clases de Castle.DynamicProxy por lo que no puedo asegurar que el assembly generado dinámicamente siempre va a ser llamado de la misma forma así como tampoco puedo asegurar que los mocks van a funcionar como se espera. Por lo que pude ver no hay problemas y hasta ahora todo el código que escribí basado en esta técnica siempre funcionó como se espera.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in Testing | Etiquetado: , | Leave a Comment »

Simplificar la depuración con el uso de DebuggerTypeProxyAttribute

Posted by Gerardo Contijoch en marzo 25, 2009

En el último post les hable sobre DebuggerDisplayAttribute, un atributo que nos permite cambiar la información que nos muestra el debugger de Visual Studio sobre un tipo. Ahora les voy a presentar otro atributo muy similar, pero cuya función es cambiar completamente lo que se muestra de un tipo, incluso sus propiedades. Estoy hablando de DebuggerTypeProxyAttribute. Al aplicar este atributo sobre una clase o estructura lo que hacemos es decirle al debugger que no use nuestra clase (o estructura) cuando inspeccionemos un objeto de ese tipo en el Debugger, sino que use un proxy a la misma. La idea detrás de esto es ocultar los miembros de nuestra clase (porque no son relevantes posiblemente) y mostrar los del proxy, los cuales son los que nos van a interesar ver cuando estemos depurando nuestro código.

Veamos como se vería un posible proxy para una clase Persona (tomada del post anterior y modificada ligeramente) junto a ella:

   1: [DebuggerDisplay("Nombre: {Nombre, nq}, Año de nacimiento: {FechaDeNacimiento.Year}, ¿Es Rosarino?: {Ciudad == \"Rosario\" ? \"Sí\" : \"No\", nq}")]
   2: public class Persona {
   3:     public string Nombre { get; set; }
   4:     public string Apellido { get; set; }
   5:     public int Edad { get; set; }
   6:     public string Ciudad { get; set; }
   7:     public DateTime FechaDeNacimiento { get; set; }
   8:
   9:     public bool EstaCasado { get; set; }
  10:     public Persona Conyuge { get; set; }
  11:
  12:     public Persona Padre { get; set; }
  13:     public Persona Madre { get; set; }
  14:
  15:     public IEnumerable<Persona> Hermanos { get; }
  16:
  17:     public bool PracticaDeportes { get; set; }
  18:     public IEnumerable<string> Deportes { get; }
  19:
  20:     public IEnumerable<string> Estudios { get; }
  21:
  22:     /// <summary>
  23:     /// Proxy para la clase Persona.
  24:     /// </summary>
  25:     internal class PersonaDebugView {
  26:         private Persona Persona { get; set; }
  27:
  28:         public PersonaDebugView(Persona p) {
  29:             Persona = p;
  30:         }
  31:
  32:         public string Nombre {
  33:             get {
  34:                 if (Persona.Madre != null) {
  35:                     return string.Format("{0} {1} {2}", Persona.Nombre, Persona.Apellido, Persona.Madre.Apellido);
  36:                 }
  37:
  38:                 return string.Format("{0} {1}", Persona.Nombre, Persona.Apellido);
  39:             }
  40:         }
  41:
  42:         public string NombreConyuge {
  43:             get {
  44:                 if (Persona.EstaCasado) {
  45:                     return Persona.Conyuge.Nombre;
  46:                 }
  47:
  48:                 return "No tiene conyuge";
  49:             }
  50:         }
  51:
  52:         public bool EsMayorDeEdad {
  53:             get { return Persona.Edad >= 21; }
  54:         }
  55:
  56:         public string PaisDeOrigen {
  57:             get { return RecuperarPaisAPartirDeCiudad(Persona.Ciudad); }
  58:         }
  59:     }
  60: }

Quisiera aclarar un par de puntos en este ejemplo. Primero, vemos que la clase proxy se encuentra dentro de la clase Persona. Esto es recomendable ya de ese modo el proxy puede tener acceso a los miembros privados de la clase que lo contiene y tiene el beneficio extra de que no se mezcla con el resto de las clases de nuestra aplicación. También es un requisito que el constructor del proxy tenga un parámetro del tipo de la clase que se desea ocultar.

Otro detalle que puede haber saltado para los que no leyeron el post anterior (¿qué están esperando?), es el uso de DebuggerDisplayAttribute. Si les interesa saber porque lo uso, pueden encontrar acá el motivo.

Si depuramos nuestra aplicación (aún sin haber aplicado el atributo DebuggerTypeProxyAttribute), veremos que la clase Persona se ve así en el debugger:

DebuggerProxyType-persona

Son muchos datos para una persona y si la mayoría de ellos no nos interesa (recordemos que estamos trabajando sobre un ejemplo) puede volverse un poco incómodo inspeccionar nuestras variables. Veamos ahora como se vería esta pantalla si aplicamos el atributo DebuggerTypeProxyAttribute de la siguiente manera:

   1: [DebuggerTypeProxy(typeof(PersonaDebugView))]
   2: [DebuggerDisplay(/*...*/)]
   3: public class Persona {
   4:     /*...*/
   5: }

DebuggerProxyType-personaconProxy

Las propiedades que estamos viendo son las del proxy (que no tienen que corresponderse necesariamente con las de la clase Persona) y nos muestran una vista totalmente diferente de la variable que estamos inspeccionando.

Presten atención a un nuevo miembro: RawView. Éste último, generado automáticamente, lo que hace es mostrarnos la vista original o sin modificar de nuestra clase y si lo abrimos veremos que tenemos acceso a todas las propiedades de la clase Persona, tal cual la definimos.

El uso de este atributo es muy común en las colecciones en donde se ocultan muchos miembros y solo se muestran unos pocos que son los más relevantes a la hora de trabajar con una colección. Siempre que inspeccionen una variable y vean el miembro RawView, van a estar viendo un proxy para esa clase y no su verdadera implementación.

Aplicando el atributo a nivel de ensamblado

Al igual que con DebuggerDisplayAttribute, DebuggerTypeProxyAttribute puede usarse a nivel de ensamblado exactamente de la misma manera. En el post anterior explico en detalle como se hace esto.

Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, Visual Studio | Etiquetado: , , | 2 Comments »

DebuggerDisplayAttribute, una gran ayuda durante la depuración

Posted by Gerardo Contijoch en marzo 19, 2009

Hace un tiempo, revisando el código fuente de Castle.Core, uno de los componentes más importantes de Castle Microkernel, redescubrí (porque me había olvidado por completo de el) uno de los atributos más útiles que existen en el .NET Framework, DebuggerDisplayAttribute, el cuál nos permite personalizar la información que se muestra de un objeto o miembro en las ventanas de debug en Visual Studio así como también en los tooltips que aparecen al pasar el cursor sobre una variable.

Imaginemos que tenemos la clase Persona definida de la siguiente manera:

   1: public class Persona {
   2:     public string Nombre { get {/*...*/} set {/*...*/} }
   3:     public int Edad { get{/*...*/} set{/*...*/} }
   4:     public string Ciudad { get {/*...*/} set {/*...*/} }
   5:     public DateTime FechaDeNacimiento { get {/*...*/} set {/*...*/} }
   6: }

Normalmente mientras estamos depurando nuestra aplicación al inspeccionar una variable de tipo Persona la ventana QuickWatch (Inspección rápida) nos muestra la variable de la siguiente manera:

DebuggerDisplayAttribute-personaSinNombre

Se puede ver que el valor (columna Value) mostrado es el propio tipo de la variable, lo cual es redundante ya que el tipo aparece aclarado en su propia columna. En nuestro caso, eso no importa mucho ya que al expandir la variable vamos a ver todas sus propiedades, que es lo que realmente nos interesa, pero imaginen que tienen una lista de personas y precisan ubicar una en particular por alguna de sus propiedades. El trabajo de encontrarla puede ser muy tedioso:

DebuggerDisplayAttribute-personasSinNombre

Por eso para ayudarnos en estos casos esta DebuggerDisplayAttribute, el cual nos permite personalizar el valor mostrado en la columna Value de modo que podamos mostrar información más útil que el tipo de la variable o miembro bajo inspección. La propiedad más importante de este atributo es Value (de tipo string) que no es ni más ni menos que el valor a mostrar en la columna Value. Esta cadena puede contener llaves ({}) entre las cuales se puede especificar el nombre de un miembro cuyo valor queremos mostrar. Veamos como podemos decorar nuestra clase con este atributo:

   1: [DebuggerDisplay("Esta persona es {Nombre}")]
   2: public class Persona {
   3:    /*...*/
   4: }

Como resultado de este cambio, ahora la clase se va a ver así en la ventana de Debug:

DebuggerDisplayAttribute-personasConNombre

Si queremos sacar las comillas de los valores de tipo string, simplemente agregamos nq al miembro al que hacemos referencia:

   1: [DebuggerDisplay("Esta persona es {Nombre, nq}")]
   2: public class Persona {
   3:     /*...*/
   4: }

DebuggerDisplayAttribute-personaConNombreSinComillas

Incluso podemos incluir expresiones simples en nuestra descripción, pero sólo si estamos trabajando con C#:

   1: [DebuggerDisplay("Nombre: {Nombre, nq}, Año de nacimiento: {FechaDeNacimiento.Year}, ¿Es Rosarino?: {Ciudad == \"Rosario\" ? \"Sí\" : \"No\", nq}")]
   2: public class Persona {
   3:     /*...*/
   4: }

DebuggerDisplayAttribute-personaConNombreYExpresion

Otra forma de uso

El hecho de usar este atributo para decorar las clases puede no ser muy bien visto ya que es algo que afecta al entorno de desarrollo y no tiene relación con el contenido de nuestras clases en si. Es ajeno a nuestros sistemas y su presencia no afecta (o al menos no debería afectar) al comportamiento de los mismos. Estamos modificando nuestras clases solo porque queremos que Visual Studio nos las muestre como a nosotros más nos gusta.

Para evitar involucrar la lógica de nuestras clases con la lógica de presentación particular de Visual Studio (otro IDE puede no soportar este atributo e ignorarlo por completo) podemos usar este atributo a nivel de ensamblado y crear un nuevo ensamblado con únicamente la declaración del atributo. De este modo, tendremos que especificar, además de la información a mostrar, la clase o estructura sobre la cual queremos aplicarla mediante la propiedad Target.

Para que quede claro esto último lo voy a explicar. La idea de usarlo a nivel de ensamblado es crear una nueva dll (proyecto de tipo Librería de clases). Este proyecto tiene que referenciar al ensamblado que contiene el tipo que queremos decorar. Esto significa que si queremos decorar el tipo XmlDocument, entonces vamos a tener que referenciar al ensamblado System.Xml.dll, en cambio, si queremos decorar nuestra clase Persona, vamos a tener que referenciar a la dll que genere nuestro proyecto.

Una vez dentro del nuevo proyecto buscamos el archivo AssemblyInfo (o cualquier otro archivo, simplemente me parece que éste es el lugar más apropiado para hacerlo) y agregamos algo similar a esto (yo voy a usar mi ejemplo):

   1: [assembly: DebuggerDisplayAttribute("Nombre: {Nombre, nq}, Año de nacimiento: {FechaDeNacimiento.Year}, ¿Es Rosarino?: {Ciudad == \"Rosario\" ? \"Sí\" : \"No\", nq}",
   2:                                     Target = typeof(DebuggerDisplayAttributeTest.Persona))]

Como se ve, usé la propiedad Target para especificar al tipo al que vamos a decorar.

Ahora solo resta compilar este ensamblado y copiarlo al path de visualizadores de Visual Studio (solo el ensamblado, no sus dependencias). Este path varia dependiendo la versión de Visual Studio y Windows. En el caso de Windows Vista, el path tiene la siguiente forma:

%userprofile%\Documents\Visual Studio [VERSION_VS]\Visualizers

Sobre Windows XP y 2003 (e imagino que en 2000 también) es el siguiente:

%userprofile%\My Documents\Visual Studio [VERSION_VS]\Visualizers

Una vez copiado el ensamblado reiniciamos Visual Studio y listo, ya tenemos nuestras clases decoradas sin necesidad de modificar su código.

Si prestan atención, en la carpeta de visualizadores van a encontrar un archivo llamado autoexp.cs. Si inspeccionan el archivo, van a ver que clases de .NET Framework fueron decoradas con este atributo. Son libres de modificarlo (¡no sin antes hacer un backup!) y agregarle todo lo que quieran, pero es responsabilidad de ustedes compilarlo en algún proyecto.

Nos vemos en el próximo post!

Referencias:

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Posted in .Net 2.0, Visual Studio | Etiquetado: , , | 1 Comment »