ASP.NET MVC: Custom Model Binders

Seguimos esa serie donde intentamos bucear un poco por algunas interioridades de ASP.NET MVC, intentando ver como funcionan por dentro algunas de las características de ese framework tan apasionante como és ASP.NET MVC. Si en el primer post de la serie vimos lo que eran los value providers y en el segundo post vimos como funcionaba el DefaultModelBinder en el post de hoy veremos como podemos crear Model Binders propios (lo que a su vez, nos ayudará a entender todavía más como funciona el DefaultModelBinder).

Bueno, para empezar dejemos claro un punto fundamental:

  • En ASP.NET MVC no estamos limitados a un solo Model Binder, podemos tener muchos model binders, de hecho uno por cada tipo de modelo.

La clase estática ModelBinders es la que mantiene el conjunto de model binders que estén registrados en el sistema. Para registrar un Model Binder propio simplemente llamamos al método Add() de la colección Binders expuesta por dicha clase:

ModelBinders.Binders.Add(typeof(FooClass), new FooBinder());

El método Add() espera el tipo de modelo y el binder a usar para objetos de dicho tipo. Si para un tipo de modelo no existe model binder definido, se usará el model binder predeterminado, cuyo valor por defecto es el DefaultModelBinder pero que también podemos cambiar:

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

1. Un ejemplo sencillo… un Model Binder para objetos simples

Vamos a crear un Model Binder propio tan sencillo como (probablemente) inútil: un model binder propio para objetos de tipo string, que simplementa convierta los valores en mayúsculas.

El trato del framework de ASP.NET MVC con los model binders es muy simple: la interfaz IModelBinder que deben implementar todos los model binders define un sólo método: BindModel. Como se las apañe el model binder internamente le da igual al framework. Por suerte para nosotros la clase DefaultModelBinder es muy extensible, de forma que cuando implementamos un model binder propio, lo más normal (aunque no es obligatorio) es derivar de dicha clase. Así que antes vamos a ver que es lo que hace el DefaultModelBinder cuando debe enlazar un modelo:

 Elace de un modelo con el DefaultModelBinder

Nota: No estan todas las funciones que usa el CustomModelBinder (faltan las relacionadas con la validación del modelo, pero no quiero hablar hoy de validaciones).

Todas estas funciones son virtuales y por lo tanto pueden ser redefinidas en clases derivadas. Aquí tienes una breve descripción de cada método:

  • BindModel: El único método de IModelBinder, su responsabilidad es devolver el modelo creado y enlazado.
  • CreateModel: Crea una instancia del modelo
  • GetTypeDescriptor: Obtiene la información del tipo del modelo
  • GetModelProperties: Obtiene información sobre las propiedades del modelo
  • BindProperty: Método que enlaza una propiedad concreta del modelo
  • GetPropertyValue: Vista en el post anterior, obtiene el valor de una propiedad. Para ello usará el model binder asociado al tipo de la propiedad y llamará a su BindModel.
  • SetProperty: Vista en el post anterior, establece el valor de una propiedad

Volviendo a nuestro caso (un model binder que convierta las cadenas en mayúsculas) vamos a derivar de DefaultModelBinder y vamos a redefinir el método BindModel. Nos basta con este, puesto que un objeto string no tiene propiedades que se puedan enlazar, así que no se llamaría nunca a BindProperty de nuestro model binder: en BindModel vamos a hacer todo el trabajo:

public class StringBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object o = base.BindModel(controllerContext, bindingContext);
return o as string != null ? ((string)o).ToUpper() : o;
}
}

El código es trivial: llamamos a la implementación de BindModel del DefaultModelBinder (de esa manera todo el trabajo va a realizarlo el CustomModelBinder) y luego simplemente convertimos el resultado a mayúsculas.

Sólo nos queda registrar nuestro model binder, usando la clase ModelBinders. Esto suele hacerse en Global.asax en el Application_Start:

ModelBinders.Binders.Add(typeof(string), new StringBinder());

Y listos… ahora cualquier cadena que se enlace será convertida a mayúsculas, pero atención! No es necesario que el controlador reciba una cadena: si la acción del controlador recibe cualquier clase que tenga una propieda de tipo string, dicha propiedad se enlazará usando nuestro model binder, por lo que dicha propiedad será convertida a mayúsculas.

2. Otro ejemplo, colecciones a partir de una sola cadena

Vamos ahora a crear un model binder para una clase de modelo específica. La clase es tal como sigue:

public class Pedido
{
public string Nombre {get; set;}
public IEnumerable<string> Bebidas {get; set;}
}

Como ya sabemos, podemos enlazar colecciones con N campos en la petición que tengan el mismo nombre, o con N campos que tengan nombre[0], nombre[1],…, nombre[N-1]. Pero en este caso, vamos a crear una vista que tenga un solo campo que se llame Bebidas:

<form action="/Pedido/Nuevo" method="post">
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<label for="Nombre">Nombre</label>
</div>
<div class="editor-field">
<input id="Nombre" name="Nombre" type="text" value="" />
</div>
<div class="editor-label">
<label for="Bebidas">Bebidas</label>
</div>
<div class="editor-field">
<input id="Bebidas" name="Bebidas" type="text" value="" />
</div>
</fieldset>
<input type="submit" value="Pedir" />
</form>

Fijaos que sólo tenemos un <input type=”text” name=”Bebidas”>. Cuando hacemos submit del formulario los datos de la petición POST quedan de la siguiente forma:

Content-Type: application/x-www-form-urlencoded
Content-Length: 51
Nombre=eiximenis&Bebidas=coca-cola%2C+fanta%2C+agua

Fijaos que existe un solo campo POST llamado Bebidas, cuyo valor es la cadena “coca-cola, fanta, agua”. Si usáis del DefaultModelBinder para enlazar este modelo, el controlador recibirá un objeto Pedido cuyo nombre será correcto, y la propiedad Bebidas será un IEnumerable<string> con un solo elemento (con valor coca-cola, fanta, agua):

image

Nota: Fíjate como el nombre se ha convertido a mayúsculas, ya que el DefaultModelBinder ha usado el StringBinder que hicimos antes para enlazar la propiedad Nombre que es de tipo String!

Bueno… esperar que el DefaultModelBinder entienda que un campo separado por comas es realmente una lista de cadenas es esperar demasiado, así que vamos a hacer un CustomBinder que haga esto:

public class PedidoBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
object value = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
object retVal = value;
if (propertyDescriptor.Name == "Bebidas" && value as IEnumerable<string> != null)
{
retVal = ((IEnumerable<string>)value).First().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
return retVal;
}
}

El código es muy simple… A diferencia del caso anterior, ahora redefinimos el método GetPropertyValue (dado que la clase Pedido si que tiene propiedades). Simplemente miramos si estamos obteniendo la propiedad “Bebidas” y si es el caso:

  1. Cogemos el valor que ha obtenido el DefaultModelBinder
  2. Lo convertimos a IEnumerable<string>, ya que sabemos que se trata de un IEnumerable<string> con una sola cadena
  3. Sobre esa cadena, devolvemos el resultado de hacer el Split (lo que devuelve un array, que a su vez es otro IEnumerable<string>).

Y listos, ahora sí que el enlace se realiza correctamente:

image

Bien, vamos a ver ahora otra manera en como podríamos codificar ese mismo model binder: vamos a implementar directamente la interfaz IModelBinder (no es lo mejor en este caso, pero vamos a aprender algunas cosillas más haciendolo).

3. Implementando IModelBinder

Insisto: En muchas ocasiones es mejor derivar de DefaultModelBinder en lugar de implementar directamente la interfaz IModelBinder (para aprovechar parte de lo que el DefaultModelBinder ya hace por nosotros).

Aquí tenemos una posible implementación:

public class PedidoBinderInterfaz : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
Pedido pedido = new Pedido();

pedido.Nombre = bindingContext.ValueProvider.GetValue("Nombre").AttemptedValue as string;
IEnumerable<string> bebidas = bindingContext.ValueProvider.GetValue("Bebidas").RawValue as IEnumerable<string>;
if (bebidas != null)
{
pedido.Bebidas = ((IEnumerable<string>)bebidas).First().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}

return pedido;
}
}

Fijate que dado que sabemos que el modelo es de tipo Pedido (puesto que este model binder sólo se registra para objetos de tipo Pedido) podemos crear directamente un Pedido y enlazar sus dos propiedades (Nombre y Bebidas).

Y ahora lo que quería que vierais: Para preguntar a los value providers por el valor de un campo de la petición, se usa el método GetValue de la propiedad ValueProvider del bindingContext. La propiedad ValueProvider es un objeto de la clase ValueProviderCollection pero nosotros lo recibimos como un IValueProvider. Eso es muy interesante: realmente el objeto es una colección de value providers, pero nosotros la vemos como un solo value provider y simplemente llamamos al método GetValue(). La propia clase se encarga de iterar por los value providers que contiene y encontrar el primero que nos pueda devolver el valor indicado.

Igual viendo el código pensáis que tampoco hay para tanto, que implementar la interfaz IModelBinder tampoco es tan complicado… bueno, fijaos en que lo que no hace este Model Binder y que si que hace el DefaultModelBinder:

  1. No estamos validando el modelo… Siempre podemos meter el código de validación dentro de BindModel y tampoco seria muy costoso hacerlo, cierto… pero perdemos la capacidad de usar DataAnnotations p.ej. (Si queremos soportar DataAnnotations entonces si que debemos empezar a tirar código)
  2. No estamos usando los model binders concretos para las propiedades… me explico: si enlazáis un modelo Pedido con este model binder, la propiedad Nombre la estamos enlazando nosotros directamente, sin usar el StringBinder que teníamos registrado… efectivamente, el valor aparece en minúsculas:

image

Bueno… espero que os esté gustando esta serie de posts sobre temas “internos” de ASP.NET MVC… aún nos quedan varias cosas por destripar!!!

Un saludo!!

PD: El código de ejemplo lo podéis descargar aquí (link a mi skydrive).

ASP.NET MVC: El DefaultModelBinder

En el post anterior vimos que eran los Value Providers de ASP.NET MVC. En éste, lo que vamos a ver es el DefaultModelBinder y algunas de sus “interioridades”…

Disclaimer: Al igual que el post anterior, este asume conocimientos básicos de ASP.NET MVC, así como de http en general.

Antes que nada el repaso rápido: Cuando un controlador recibe en una acción un objeto del modelo, ASP.NET MVC es capaz de realizar el binding entre los datos contenidos en la request y el objeto que espera el controlador. Por un lado los value providers se encargan de leer los datos de la request y guardarlos “en una estructura común” y por otro los model binders crean el objeto del modelo a partir de dicha estructura común.

Hoy vamos a diseccionar el DefaultModelBinder, el model binder que trae por defecto ASP.NET MVC.

Colecciones

Vamos a suponer la siguiente clase del modelo:

public class Persona
{
public IEnumerable<string> Telefonos { get; set; }
public string Nombre { get; set; }
public int Edad { get; set; }
}

Asumid que tenemos un controlador Personas con una acción Crear que recibe un objeto Persona. Vamos a ver como el DefaultModelBinder es capaz de mapear las propiedades, incluída la colección Telefonos. Para ello tengo simplemente la siguiente vista:

<form action="/Personas/Crear" method="post" >
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<label for="Nombre">Nombre</label>
<input type="text" name="Nombre" />
</div>
<div class="editor-label">
<label for="Edad">Edad</label>
<input type="text" name="Edad" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 1</label>
<input type="text" name="Telefonos" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 2</label>
<input type="text" name="Telefonos" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 3</label>
<input type="text" name="Telefonos" />
</div>
<input type="submit" />
</form>

Fijaos que hay 3 <input type=”text” name=”Telefono”>. Esto no es incorrecto, el parámetro name de un <input> puede estar repetido para indicar campos con más de un valor… Si introduzco datos y envío el formulario los campos POST de la request son:

Content-Type: application/x-www-form-urlencoded
Content-Length: 90
Nombre=Nombre&Edad=12&Telefonos=%2B34+555222&Telefonos=%2B34+666112&Telefonos=%2B34+777114

Honestamente… el navegador no es que haga gran cosa: simplemente tenemos 5 campos POST: Nombre, Edad y 3 campos Telefonos. Veamos ahora lo que ocurre en el lado del DefaultModelBinder.

El método dentro del DefaultModelBinder que obtiene el valor de una propiedad se llama GetPropertyValue y tiene la siguiente firma:

protected virtual object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)

De todos los parámetros que recibe este método nos interesan básicamente dos:

  1. bindingContext: Un objeto de la clase ModelBindingContext que contiene la información necesaria para poder realizar el binding: Proporciona acceso a los value providers, al ModelState (donde se guarda información relativa a la validación del modelo) y a los metadatos del modelo.
  2. propertyDescriptor: Un objeto de la clase PropertyDescriptor con información sobre la propiedad del modelo de la cual queremos obtener el valor (básicamente el tipo de la propiedad).

A partir de la información del bindingContext el DefaultModelBinder puede preguntar a los value providers que le den el valor del campo que contiene el valor de la propiedad (por defecto si estamos rellenando la propiedad Telefonos el DefaultModelBinder preguntará a los value providers por el valor de Telefonos). El DefaultModelBinder usará la información del propertyDescriptor para convertir el valor devuelto por los value providers al tipo del que sea la propiedad.

Luego el framework llama al método SetPropertyValue del propio model binder, que tiene esta firma:

protected virtual void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)

Los parámetros son más o menos los mismos que GetPropertyValue, con la salvedad de que recibimos también el objeto devuelto por GetPropertyValue. En este método “simplemente” asignamos el valor a la propiedad (podemos acceder al modelo a través del la propiedad Model del parámetro bindingContext).

Bueno, en el caso de la petición POST que nos ocupa esto es lo que devuelve el método GetPropertyValue de la propiedad Teléfonos:

image

El DefaultModelBinder, a partir de la información de los value providers, crea un array de cadenas con el valor de los tres teléfonos (recordad que la propiedad estava declarada como IEnumerable<string> en el modelo).

Hemos visto como a partir de una petición POST con varios campos “Telefonos” el DefaultModelBinder era capaz de enlazar esto a una variable IEnumerable<string> del modelo… Sigamos jugando un poco a ver que pasa… 🙂

Imaginad que hago la siguiente modificación en la vista:

<div class="editor-label">
<label for="Telefonos">Telf 1</label>
<input type="text" name="Telefonos[0]" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 2</label>
<input type="text" name="Telefonos[1]" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 3</label>
<input type="text" name="Telefonos[2]" />
</div>

Es decir, en lugar de tener tres campos cuyo name es Telefonos, tengo tres campos cuyo name es Telefonos[0], Telefonos[1] y Telefonos[2]. Si relleno los datos y envio la petición, ahora los datos POST tienen la forma:

Content-Type: application/x-www-form-urlencoded
Content-Length: 105
Nombre=eiximenis&Edad=20&Telefonos%5B0%5D=%2B34+111&Telefonos%5B1%5D=%2B34+222&Telefonos%5B2%5D=%2B34+333

Ahora en los datos POST tenemos (además de Nombre y Edad) tres campos más, llamados Telefonos%5B0%5D, Telefonos%5B1%5D, y Telefonos%5B2%5D (%5B es el código para [ y %5D es el código para ]).

Pues bien: eso funciona correctamente… el DefaultModelBinder en el GetPropertyValue para la propiedad Telefonos hace lo siguiente (es una simplificación de lo que ocurre realmente, pero es suficiente para entender el concepto):

  1. Pregunta a los value providers para el valor de Telefonos y el resultado es null (no hay ningún campo “Telefonos”)
  2. Como el DefaultModelBinder sabe que está enlazando una colección, pregunta a los value providers por el valor de Telefonos[0] y si existe lo añade a la colección de la propiedad Telefonos y pregunta por Telefonos[1]… y así sucesivamente.

No me puteéis el pobre DefaultModelBinder, que es bastante inteligente pero tampoco es dios… eso no funciona:

<div class="editor-label">
<label for="Telefonos">Telf 1</label>
<input type="text" name="Telefonos[1]" />
</div>
<div class="editor-label">
<label for="Telefonos">Telf 2</label>
<input type="text" name="Telefonos[2]" />
</div>

En este caso NO tenemos ni campo “Telefonos”, ni campo “Telefonos[0]» en la request, así que el DefaultModelBinder entenderá que la propiedad Telefonos no ha sido informado y la pondrá a null.

Objetos compuestos

Imaginad ahora que tenemos nuestro modelo definido del siguiente modo:

public class Persona
{
public IEnumerable<string> Telefonos { get; set; }
public string Nombre { get; set; }
public int Edad { get; set; }

public Direccion Direccion { get; set; }
}

public class Direccion
{
public string Calle { get; set; }
public string Ciudad { get; set; }
}

Como se las apaña el DefaultModelBinder para enlazar la propiedad Direccion que es un objeto completo??

Para ver como se las puede apañar el DefaultModelBinder, vamos a dejar que el propio framework nos ayude 🙂 Par ello añado las siguientes líneas dentro del <form> de la vista:

<div class="editor-label">
<%: Html.LabelFor(x=>x.Direccion) %>
<%: Html.EditorFor(x=>x.Direccion) %>
</div>

Recordad que Html.LabelFor lo que hace es crear una <label> vinculada a la propiedad del modelo que se le pasa vía la expresión lambda. Por otro lado Html.EditorFor lo que hace es crear un “editor” para la propiedad del modelo que se le indica… El framework usa los metadatos del modelo para saber que tipo de editor es mejor (además de que nosotros podemos definir el tipo editor que queramos, pero eso es otra historia). Cuál creeis que es el “editor por defecto” para la propiedad Direccion del modelo, que es un objeto de la clase Direccion?

Si habéis respondido “dos textboxes, uno para la propiedad Calle y el otro para la propiedad Ciudad” habeis dado en el clavo… este es el código HTML que me genera la llamada a Html.EditorFor:

<div class="editor-field">
<input class="text-box single-line" id="Direccion_Calle" name="Direccion.Calle" type="text" value="" />
</div>
<div class="editor-label">
<label for="Direccion_Ciudad">Ciudad</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Direccion_Ciudad" name="Direccion.Ciudad" type="text" value="" />
</div>

Exacto, dos <input type=”text”> uno para la propiedad Calle y el otro para la propiedad Ciudad… pero os habéis fijado en los name?

  • Direccion.Calle
  • Direccion.Ciudad

No vamos a entrar dentro del ciclo de vida entero del DefaultModelBinder (porque daría para otro post entero) y pienso que nos basta con esta idea: El DefaultModelBinder es capaz de procesar modelos complejos siempre y cuando los value providers tengan valores cuyo nombre sea propiedad.propiedad.propiedad…

Validación del modelo

El DefaultModelBinder no sólo crea objetos del modelo y los enlaza… también es el responsable de validar que el modelo cumpla con las restricciones que tiene. Las restricciones forman parte de los metadatos del modelo y por lo tanto están accesibles a través del parámetro bindingContext (propiedad ModelMetaData). El DefaultModelBinder comprueba que los valores de las propiedades satisfacen las restricciones que indican los metadatos. Si no es el caso, utiliza el ModelState (accesible a través del bindingContext) y añade un error (llamando al método AddModelError) para indicar que la propiedad determinada no cumple la restricción indicada.

El proveedor de metadatos para el modelo que usa ASP.NET MVC por defecto es DataAnnotations, pero nosotros podríamos crearnos nuestros propios proveedores de metadatos (sí, sí… ASP.NET MVC es sumamente extensible). Pero eso también ya es una historia para otro post… 🙂

Resumiendo…

El Model Binder es el encargado de crear un objeto del tipo correspondiente al que espera la acción de destino del controlador y de rellenarlo con los valores que provienen de la petición, aunque para ello no consulta los datos de la petición, sinó que consulta los datos de los value providers (que son quienes previamente han consultado la petición). Eso independiza al Model Binder de la forma en cómo los datos estén codificados en la petición. Finalmente el Model Binder también valida que el modelo cumpla las restricciones indicadas en los metadatos asociados.

Hemos visto por encima como funciona el DefaultModelBinder, el Model Binder que viene por defecto en ASP.NET MVC… Como (casi) todo en ASP.NET MVC podemos cambiar el DefaultModelBinder por otro a nuestro antojo, o lo que suele ser más normal, asociar un Model Binder específico a un tipo de modelo… Pero eso lo veremos en otro post… 🙂

Un saludo!

ASP.NET MVC: ValueProviders

Hola! Hoy quiero comentar un aspecto de ASP.NET MVC2 que no sé hasta que punto es conocido, y son los llamados Value Providers.

Disclaimer: Este post será largo y puede ser un poco denso y asumo conocimientos básicos de ASP.NET MVC. Tampoco tengas reparos en leerte este post en más de un dia si quieres… había pensado dividirlo en dos posts, pero al final he preferido meterlo todo en uno.

Si habéis trabajado un poco con ASP.NET MVC, sabréis que si tenéis un controlador con esta acción:

[HttpPost()]
public ActionResult Create(Producto p)
{
// Hacer algo con producto
return View();
}

ASP.NET MVC es capaz de hacer binding entre los parámetros de la Request y las propiedades de la clase Producto para instanciar un objeto Producto para tí y pasarlo al controlador.

Es decir, si la clase producto está definida como:

public class Producto
{
public int Codigo { get; set; }
public string Nombre { get; set; }
public int Precio { get; set; }
}

Y la Request tiene estos campos, ASP.NET MVC hace el binding por nosotros… P.ej. si usamos un formulario como el siguiente para enviar los datos:

<form action="/Productos/Create" method="post">
<fieldset>
<input id="Codigo" name="Codigo" type="text" value="" />
<input id="Nombre" name="Nombre" type="text" value="" />
<input id="Precio" name="Precio" type="text" value="" />
<input type="submit" value="Create" />
</fieldset>
</form>

La Request tiene los campos “Codigo”, “Nombre” y “Precios” (los name de los input). Al enviarse el formulario genera una Request con los siguientes datos POST (se pueden ver fácilmente usando firebug):

Content-Type: application/x-www-form-urlencoded

Content-Length: 31

Codigo=1&Nombre=Silla&Precio=12

La última línea es la que tiene los campos con los valores, que son usados por ASP.NET MVC para realizar el binding con la clase Producto.

El responsable de recoger los valores y realizar el binding con el modelo es el ModelBinder pero no es reponsabilidad del ModelBinder saber donde están los valores. P.ej. haced una prueba… Si al formulario anterior le quitáis uno de los campos (p.ej. el campo Precio) y modificáis el action del <form> para que quede <form action=”/Productos/Create?Precio=10” method=”post> y realizáis la petición veréis que sigue funcionando.

En este caso si miramos con firebug la petición, no tiene el parámetro post “Precio”:

Content-Type: application/x-www-form-urlencoded

Content-Length: 21

Codigo=1&Nombre=Silla

Pero si que dicho parámetro está en la URL, que ahora es /Productos/Create?Precio=10. Pero esto no afecta al ModelBinder que es capaz de recoger el parámeteo con independencia de dónde se encuentre dentro de la Request. ¿Y cómo es posible? Pues gracias a los Value Providers.

Pero antes sigamos jugando un poco, para tener todas las piezas encima de la mesa… que ocurre si sin modificar el atributo action del tag <form> volvemos a añadir el <input name=”Precio”> al formulario? Es decir tenemos un formulario con los tres campos (Codigo, Nombre y Precio) pero además tenemos el campo Precio otra vez en la url (?Precio=100). Pueden ocurrir tres cosas:

  1. Que ASP.NET MVC de error, porque el campo “Precio” está repetido (una vez en la URL y otra en los parámetros POST) de la petición.
  2. Que tenga prioridad el valor del parámetro en la URL
  3. Que tenga prioriodad el valor del parámetro POST

Bien, lo que ocurre es que tiene prioridad el valor del parámetro POST. Es decir el valor de la propiedad Precio del objeto Producto que reciba el controlador será el que haya entrado el usuario y no el que se indica en la URL. Pero… ¿por que?

Cuando una petición es tratada por el framework, primero se pasa por una serie de value providers que inspeccionan cada uno dicha petición y guardan los valores que cada uno de ellos entiende. P.ej. existe un value provider para inspeccionar los valores POST y otro distinto para inspeccionar los valores de la URL. ASP.NET MVC mantiene una colección de value providers y pasa la request por todos ellos.

P.ej. asumamos en nuestro caso que tenemos una colección con dos value providers (luego veremos que esto no es exactamente así, pero me vale esa simplificación por ahora). Llamemosle PostValueProvider al primero y UrlValueProvider al segundo. Supongamos que nos llega la petición que hemos comentado:

  • Parámetros en la URL: ?Precio=100
  • Parámetros POST: Codigo=1&Nombre=Silla&Precio=12

La request pasa por el primer value provider (supongamos que es el PostValueProvider) que la inspecciona, ve que hay tres parámetros llamados Código, Nombre y Precio y se los guarda con sus respectivos valores. Luego la request pasa por el siguiente value provider, el UrlValueProvider que inspecciona la request y ve que hay un parámetro llamado Precio y se lo guarda junto con su valor.

Ahora es el turno del ModelBinder: el ModelBinder detecta que debe crear un objeto Producto que tiene tres propiedades: Código, Nombre y Precio, así que pregunta a los value providers por los valores de estos campos. Esta frase es la clave: pregunta a los value providers, no a un value provider en particular. Pongamos que cuando el ModelBinder necesita el valor del campo “Precio” se limita a preguntar simplemente el valor de dicho campo, y el ModelBinder espera que haya un sólo valor para dicho campo. Si como es el caso dos value providers han guardado el valor para dicho campo, sólo uno responde… cual? Pues el que esté primero en la lista de value providers que ASP.NET MVC mantiene. Ni más ni menos 🙂

Creación de un Value Provider propio

Como ya sabréis ASP.NET MVC es muy extensible, así que obviamente uno se puede crear sus propios Value Providers, para permitir tratar peticiones que tengan datos codificados de forma extraña o en otros sitios donde pueden estar los datos (p.ej. en cookies). Crear un value provider es muy simple, basta crear una clase que implemente la interfaz IValueProvider. Este interfaz define dos métodos:

  1. ContainsPrefix –> No se como explicar fácilmente lo que significa exactamente sin entrar en demasiados detalles sobre como funciona el ModelBinder, así que permitidme una simplificación. Este método devuelve si el value provider tiene valor para un campo en concreto. Es decir si el value provider tiene un valor para el campo “Precio” entonces ContainsPrefix(“Precio”) debe devolver true. Insisto: No es tan simple, pero para entender el concepto del post nos basta así.
  2. GetValue –> Devuelve el valor que se le pide. Es decir GetValue(“Precio”) devuelve el valor correspondiente al campo “Precio” que tenga este Value Provider. Este método sólo se llama si ContainsPrefix devuelve true.

Vamos a ver como podemos implementar un ValueProvider propio… P.ej. vamos a implementar un Value Provider que lea valores de los <appSettings> del web.config (de acuerdo, quizá no es brutalmente útil, pero como ejemplo servirá).

El código podría ser el siguiente:

public class AppSettingsValueProvider : IValueProvider
{
private Dictionary<string, ValueProviderResult> values;


public AppSettingsValueProvider()
{
values = new Dictionary<string, ValueProviderResult>();
foreach (string key in ConfigurationManager.AppSettings.AllKeys)
{
string appSetting = ConfigurationManager.AppSettings[key];
values.Add(key, new ValueProviderResult(appSetting, appSetting, CultureInfo.InvariantCulture));
}
}

public bool ContainsPrefix(string prefix)
{
return values.ContainsKey(prefix);
}

public ValueProviderResult GetValue(string key)
{
ValueProviderResult value;
values.TryGetValue(key, out value);
return value;
}
}

En el constructor se inicializa el diccionario values con los valores leídos de los <appSettings> del web.config. El método GetValue de IValueProvider, no devuelve un object con el valor directo del campo pedido, sinó que devuelve un ValueProviderResult una clase que tiene tres campos (rawValue, attemptedValue y culture).  El campo rawValue es lo que realmente se ha leído de la petición, mientras que el campo attemptedValue es el valor de rawValue convertido a una string. En mi caso dado que <appSettings> contiene ya string, tanto rawValue como attemptedValue tienen el mismo valor.

Bien! Ya tenemos un value provider… ahora ha llegado el momento de decirle a ASP.NET MVC que lo use… y aquí es donde debemos explicar la simplificación que hice antes 🙂

Factorías de Value Providers

Os acordáis cuando dije que ASP.NET MVC mantenía una colección de value providers y que pasaba la request a cada uno de ellos para que pudiesen procesarla y guardarse los campos necesarios? Dije que esto no era exactamente así, sinó una simplificación… Pues bien, la verdad es que ASP.NET MVC no mantiene una colección de value providers, sinó una colección de factorías de value providers.

Si te preguntas porque una factoría en lugar de guardar directamente los value providers… es para darte más control sobre como se crean los value providers: un value provider actúa sobre los datos de la petición actúal, por lo que por cada petición deben crearse todos los value providers de nuevo… La interfaz IValueProvider no define ningún mecanismo para pasarle la Request al value provider. Tampoco tenemos acceso a ningún tipo de contexto ni nada parecido… Piensa, si ASP.NET MVC debe crear los value providers a cada petición, cómo le pasa los datos de la request?

La solución pasa por usar factorías de value providers es decir, clases cuya única responsabilidad es crear los value providers a cada petición.

Veamos como sería nuestra factoría que cree objetos de nuestro AppSettingsValueProvider:

public class AppSettingsValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{

return new AppSettingsValueProvider();
}
}

Basta con derivar de ValueProviderFactory y redefinir el método GetValueProvider. En este méotdo tenemos acceso al ControllerContext que a su vez nos da acceso a la request http. Ahora si queréis pasáis la request http (o lo que queráis) al value provider.

Una vez tenemos la factoría debemos decirle a ASP.NET MVC que la use. Eso se hace registrando nuestra factoría usando la clase estática ValueProviderFactories:

ValueProviderFactories.Factories.Add(new AppSettingsValueProviderFactory());

Usualmente esto se coloca en el Application_Start de Global.asax.

Fijaos en un detalle: La factoría se crea sólo una vez (y la creamos nosotros, no el framework) y luego para cada petición se llama al método GetValueProvider de la factoría… método que también hemos hecho nosotros y donde se devuelve el value provider. De esta manera controlamos la creación de los value providers (lo que nos permitiría usar, si quisiéramos, mecanismos de inyección de dependencias).

Y listos! Ya estamos. Si modificamos la vista de datos para que no tenga el campo “Precio” (ni como POST ni como GET) y añadimos en el web.config:

<appSettings>
<add key="Precio" value="999"/>
</appSettings>

Veréis que el controlador recibe un Producto con el nombre y código que hayáis indicado y un Precio de 999. Dado que nuestra factoría se ha añadido la última de la lista, si volvéis a poner el Precio en el formulario, tendrá prioridad el que haya entrado el usuario (ya que la factoría que crea el value provider con los datos POST está antes de nuestra factoría).

Os dejo un proyecto de demo. La vista inicial muestra tres enlaces: Formulario con los tres campos, formulario con dos campos pero precio en la url y formulario con dos campos. Al hacer submit del formulario se nos muestran los detalles del producto entrado. Veréis como en el último caso (formulario con dos campos) aparece el valor de precio 999 que es el que se ha sacado del web.config (en el primer caso tiene preferencia el valor entrado por el usuario y en el segundo el valor de la url).

Os podeis descargar el proyecto desde aquí (enlace a mi skydrive).

Espero que te haya quedado más o menos clara la idea de los value providers… en un post próximo hablaré de los ModelBinders para que podamos tener la foto completa!

Saludos!

Evento de ASP.NET MVC

Hola a todos!! Mañana (o sea jueves 6 de mayo) hay una charla en las oficinas de Microsoft en Madrid sobre ASP.NET MVC, en la cual tengo el gusto de participar.

Yo voy a contar cosillas sobre controladores y modelos… Veremos como hacer binding y validaciones de modelos, un poco sobre factorías de controladores, action results, action filters y cuatro cosillas sobre la tabla de rutas… En fin, lo que me de tiempo en una hora. 🙂

Os recomiendo que si podéis y estáis interesados en ASP.NET MVC os paséis por allí, ya no tanto para escucharme a mí, sinó para escuchar a otros cracks como Hadi Hariri o Miguel Caballero que también participan en la charla!!

La agenda y los detalles de la charla estan aquí: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032444497&Culture=es-ES

Un saludo a todos!

[ASP.NET MVC] ‘formElement is null’ o como un pequeño error te hace perder el tiempo…

Hola! Esta es una breve historia de un pequeño error que cometí y que quiero compartir con vosotros… por si acaso 🙂

Tenía una aplicación ASP.NET MVC que usaba DataAnnotations para la validación de los modelos. El uso de DataAnnotations para permitir la validación de modelos en ASP.NET MVC es muy simple. El primer paso es decorar la clase modelo con los atributos que indican las validaciones a realizar:

public class FooModel
{
[Required(ErrorMessage="Debe entrar un valor!")]
public string Name { get; set; }
}

Aquí simplemente indicamos que la propiedad ‘Name’ debe ser obligatoria. Hay otros validadores como RegularExpressionAttribute que valida contra una expresión regular o RangeAttribute que valida entre rangos, entre otros (y obviamente nos podemos crear los nuestros).

La vista para editar elementos de tipo FooModel es también muy sencilla:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<FooMvc.Models.Home.FooModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

<h2>Index</h2>

<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>

<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Name) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Name) %>
<%: Html.ValidationMessageFor(model => model.Name) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
</asp:Content>

Fijaos en la llamada a Html.EnableClientValidation() que lo que hace es habilitar las validaciones en cliente (además de las de servidor claro, recordad que siempre debe validarse en servidor con independencia de que también se valide en cliente). Si no usamos EnableClientValidation() tenemos sólo validación en servidor. El uso de la validación en cliente requiere el uso de la ajax library, así que en mi master page he incluído lo siguiente:

<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcValidation.js" type="text/javascript"></script>

Finalmente queda sólo el controlador… Es extremadamente simple:

public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}

[HttpPost()]
public ActionResult Index(FooModel model)
{
if (ModelState.IsValid)
{
// Validación OK
}
else
{
return View(model);
}
}

}

El método Index sin parámetros es el que responde a la petición GET /host/Home/Index y devuelve la vista. Cuando se hace submit del formulario, se llama a /host/Home/Index pero usando POST por lo que se usa el método Index que recibe un FooModel. ASP.NET MVC hace binding automáticamente entre los parámetros POST y las propiedades de FooModel, por lo que obtenemos el objeto con los datos del usuario. La propiedad ModelState.IsValid me devuelve si las validaciones son correctas o no (y si no lo son, generalmente lo que se hace es mostrar de nuevo la vista para que el usuario pueda corregir los datos).

Hasta ahí todo normal… sólo que a mi no me funcionaba. Recordad que había habilitado las validaciones en cliente, así que si hacía submit del formulario sin entrar ningún valor para la propiedad Name, me debería mostrar el error sin hacer POST… pero me lo hacía. Es decir, me ignoraba las validaciones en cliente.

Después de varios intentos y mirando que podía estar pasando, lo ejecuté con Firefox para ver si es que se daba algún error de javascript (internet explorer no me decía nada). Y efectivamente en la consola de javascript de Firefox me aparecía un error: formElement is null.

Una búsqueda en Google no me dio muchos resultados… A alguien más le pasaba lo mismo pero ninguna de las respuestas eran aplicables a mi caso. Después de algunas paranoias (era mi primer proyecto asp.net mvc2 con el vs2010 rtm y a lo mejor algo no se había instalado bien), investigando el código html generado vi esto:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNDEyOTQxNTc1ZGRm6ES0w4WmJi1Rd8IlwFU8wFLynCBA/haT9Dce2tSlxQ==" />

Coooomo??? Un Viewstate??? Y que se supone que hace aquí? Bueno… Pues sabéis de donde venía? Pues de aquí:

<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder ID="MainContent" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>

Y donde estaba este maléfico <form runat=”server”>? Pues sí, sí: en la master page.

Y por qué? Bueno… pues porque cuando añadí la master page me equivoqué y añadí una “Master Page” en lugar de una “MVC 2 View Master Page”:

image

Así que esa es mi historia de como un error tonto puede llegar a darte verdaderos quebraderos que cabeza… 🙂

Un saludo!

PD: Y ahora voy a escribir 100 veces “cuando añada una master page a mi proyecto, voy a fijarme que sea una master page de MVC 2”… 🙂

Var, object y dynamic

Hola a todos! El otro día me preguntaban sobre las diferencias entre usar var, object y dynamic, y por lo que he podido observar no todo el mundo tiene claro que diferencias hay en cada caso, de ahí que me haya decidido escribir este post.

1. Inferencia de tipos (var)

Para ver el uso de var lo mejor es un ejemplo:

var i = 10;         // Ok
int i2 = i + 1; // Ok
i = "20"; // error CS0029: Cannot implicitly convert type 'string' to 'int'
string s = i; // error CS0029: Cannot implicitly convert type 'int' to 'string'
var j; // error CS0818: Implicitly-typed local variables must be initialized
var k = null; // error CS0815: Cannot assign <null> to an implicitly-typed local variable

La palabra clave var declara una variable cuyo tipo es inferido a partir de la expresión que se le asigna. Así de sencillo. Quizá hay gente que asume que var declara una variable dinámica debido a la influencia de javascript. Pero el var de C# no tiene nada que ver con el var de javascript. P.ej. en C++ se usa la palabra clave auto en lugar de var (de todos modos si buscas información sobre la palabra clave auto en C++ vigila ya que antes tenía otro significado (que casi nadie utilizaba)).

Si analizamos el código anterior vemos que la línea var i=10 declara una variable i cuyo tipo se infiere de la expresión que se le asigna. Dado que la expresión 10 se resuelve a tipo int, la variable i es de tipo int. Podemos ver que podemos asignar i a otra variable de tipo int, pero no podemos asignar i a una variable de tipo string, ni tampoco asignar un string a i (la variable i no es de tipo dinámico y por lo tanto no puede cambiar de tipo).  También podemos observar que no podemos declarar una variable con var sin asignarle expresión (lógico puesto que entonces el compilador no sabe el tipo de dicha variable) y que tampoco la podemos declarar asignándole null (lógico porque null no tiene tipo).

Si os preguntáis para que existe var, no es (sólo) para complacer a los perezosos sino para dar soporte a los tipos anónimos.

2. Dynamic vs object

Visual Studio 2010 viene con la nueva palabra clave dynamic que ahora sí nos permite declarar una variable de tipo dinámico:

dynamic i = 10;         // Ok
int i2 = i + 1; // Ok
i = "20"; // Ok
string s = i; // Ok
dynamic j; // Ok
dynamic k = null; // Ok

Ahora nuestra variable i es de tipo dinámico y es por ello que le podemos asignar un int (como en la primera línea) o bien una cadena (como en la tercera) y del mismo modo podemos asignar la variable i a una cadena. Ojo! Que podamos asignar la variable i a una cadena no significa que sea válido hacerlo: en tiempo de ejecución se realiza la transformación y puede ser que nos de un error. P.ej. el siguiente código compila pero (obviamente) da una excepción en ejecución:

dynamic i = 10;
Guid guid = i;

Si lo probamos vemos que sí, que compila, pero en ejecución nos lanza la excepción RuntimeBinderException con el mensaje Cannot implicitly convert type ‘int’ to ‘System.Guid’.

Veamos ahora más temas interesantes sobre dynamic. Por ejemplo, que creeis que imprime por pantalla el siguiente código:

dynamic i = 10;
Console.WriteLine(i.GetType().FullName);
i = "20";
Console.WriteLine(i.GetType().FullName);

Pues esto:

System.Int32

System.String

Es decir, vemos que aunque la variable i se haya declarado como dynamic, cuando se ejecuta el método GetType() se ejecuta sobre el objeto real contenido por i.

Alguien puede decir que si en el código anterior cambiamos dynamic por object el resultado es idéntico… Entonces… ¿donde está la diferencia? ¿Cuando debo usar dynamic?

Bien, simplificando podemos asumir lo siguiente: En tiempo de ejecución las variables dynamic se traducen a object (el CLR no entiende de dynamic). Pero cuando usamos dynamic el compilador desactiva toda comprobación de tipos, cosa que no ocurre cuando usamos object. Compara los dos códigos:

// Compila
dynamic d = "eiximenis";
string str = d.ToUpper();
// NO compila
object d2 = "eiximenis"; // Ok
string str2 = d2.ToUpper(); // error CS1061: 'object' does not contain a definition for 'ToUpper' and no extension method 'ToUpper' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

El primer código compila mientras que el segundo no, puesto que aunque la variable d2 contiene un objeto de tipo string, la referencia es de tipo object y object no contiene ningún método ToUpper. Mientras que en el caso de dynamic el compilador asume que sabemos lo que estamos haciendo, así que desactiva la comprobación de tipos y listos… Por supuesto si en tiempo de ejecución el objeto referido por la variable dinámica no contiene el método especificado… excepción al canto.

O sea que dynamic no es más que un truco que nos proporciona el compilador: el CLR no sabe nada de dynamic, es el compilador de C# quien hace toda la magia. ¿Quieres otro ejemplo de ello? Aquí lo tienes:

List<dynamic> lst = new List<dynamic>();
List<object> lst2 = new List<object>();
bool b = lst.GetType() == lst2.GetType();
// b vale true

La variable b vale true porque en tiempo de ejecución, tanto lst como lst2 son una List<object>, dado que dynamic se traduce en tiempo de ejecución por object.

3. DLR

Bueno… hemos dicho que cuando usamos dynamic, el compilador lo que hace es básicamente declarar la variable como object y suspender su comprobación de tipos… pero que más hace? Es decir, como traduce:

d.foo();    // d es dynamic

Siendo d una variable declarada como dynamic.

Lo que podría hacer el compilador es simplemente “no traducirlo por nada”, es decir generar el mismo código (IL) como si d fuese una variable tradicional. P.ej. dado el siguiente código C#:

int i = 0;
i.ToString();

El compilador lo traduce en el siguiente código IL (se puede ver con ildasm):

// int i=0;
ldc.i4.0 // Cargamos el valor 0 a la pila
stloc.0 // Sacamos el top de la pila y lo guardamos en la var #0 (i)
// i.ToString();
ldloca.s i // Ponemos la dirección de la variable #0 (i) en la pila
// Llamamos al método ToString. El valor the 'this' se obtiene del top de la pila
call instance string [mscorlib]System.Int32::ToString()

Una opción que tendría el compilador si i estuviese declarada como dynamic en lugar de int seria generar el mismo IL, es decir una llamada tradicional a call. Si en tiempo de ejecución el método indicado no se encuentra en la clase, el CLR da un error.

Otra opción que tiene el compilador es usar Reflection, es decir traducir la llamada d.foo(); a un código parecido a:

// código original es d.foo();
var mi = d.GetType().GetMethods().FirstOrDefault(x => x.Name.Equals("foo"));
object retval = mi.Invoke(d, null);

Este segundo método es más elegante puesto que el compilador podría añadir código propio para gestionar los errores (p.ej. comprobar si mi es null). De hecho el primer método (no traducir nada y generar un IL parecido al que hemos visto) sería muy bestia ya que estamos confiando en la seguridad del CLR y no es esa su función.

Bueno… supongo que si te imaginas que si te estoy metiendo ese rollo es para decirte que el compilador no usa ninguna de esas dos opciones. En su lugar utiliza llamadas al DLR. ¿Y que es el DLR? Pues un conjunto de servicios (construídos encima del CLR) para añadir soporte a lenguajes dinámicos en .NET.

Te puedes preguntar porque necesitamos el DLR y no podemos usar simplemente Reflection. Bien, aunque con Reflection podemos simular llamadas dinámicas, los lenguajes dinámicos permiten más cosas, como p.ej. añadir en tiempo de ejecución métodos a clases o objetos ya existentes. Hacer esto con Reflection es imposible, puesto que Reflection nos permite invocar cualquier miembro de una clase, pero dicho miembro debe estar definido en la clase cuando esta se crea (no se pueden añadir miembros en tiempo de ejecución).

Así pues dado que tenemos al DLR que nos ofrece soporte para tipos dinámicos, el compilador de C# usa llamadas al DLR cuando debe resolver llamadas a miembros de objetos contenidas en variables declaradas como dynamic. Así pues podemos ver que una referencia dynamic se traduce en tiempo de ejecución (gracias al compilador) en una referencia object pero que usará el DLR para acceder a sus miembros.

4. ExpandoObject

Vamos a ver un poco el poder del DLR en acción. Y un ejemplo sencillo y rápido es la clase ExpandoObject.

La clase ExpandoObject representa un objeto al que en tiempo de ejecución se le pueden añadir o quitar propiedades y/o métodos. Fíjate en el siguiente código:

static void Main(string[] args)
{
dynamic eo = new ExpandoObject();
eo.MiPropiedad = 10;
eo.MiOtraPropiedad = "Cadena";
Dump(eo);
Console.ReadLine();
}

static void Dump(dynamic d)
{
Console.WriteLine("MiPropiedad:" + d.MiPropiedad);
Console.WriteLine("MiOtraPropiedad:" + d.MiOtraPropiedad);
}

Creamos un ExpandoObject y luego creamos las propiedades MiPropiedad y MiOtraPropiedad. Crear una propiedad en un ExpandoObject es tan simple como asignarle un valor (ojo! la propiedad sólo se crea cuando se asigna un valor a ella, no cuando se consulta). Luego en el método Dump consultamos dichas propiedades y obtenemos sus valores.

Aquí el uso de dynamic es obligatorio: No podemos declarar la variable eo como ExpandoObject ya que entonces no podemos “añadir propiedades”. Al declarar la variable como dynamic, hacemos que el código compile (el compilador no comprueba que existan las propiedades) y que se use el DLR para llamar a las propiedades MiPropiedad y MiOtraPropiedad. La clase ExpandoObject se integra con el DLR (a través de la interfaz IDynamicMetaObjectProvider) y eso es lo que permite que se añadan esas propiedades al objeto en cuestión.

Resumiendo pues, hemos visto que var simplemente sirve para declarar variables cuyo tipo se infiere de la expresión que se les asigna (necesario para poder asignar un objeto anónimo a una variable) mientras que dynamic es el mecanismo que tenemos en C# para declarar una variable, para la cual el compilador suspenda la comprobación de tipos por un lado y que genere código para usar el DLR por otro.

Saludos!

Diseñar clases para ser heredadas…

Una de las ventajas de la programación orientada a objetos, es la herencia de clases y el polimorfismo: eso es la capacidad para crear clases derivadas a partir de otras clases y poder usar las clases derivadas en cualquier lugar donde se espere la clase base.

El comportamiento por defecto de C# (y VB.NET) es que cuando creamos una clase, esa se puede exteder, es decir puede crearse una clase derivada. Debemos declarar explicitamente la clase como sellada (sealed) para impedir que alguien cree una clase derivada a partir de la nuestra. Es una buena práctica declarar tantas clases sealed como sea posible (al menos las clases públicas, para las internas no son necesarias tantas precauciones ya que no serán visibles desde fuera de nuestro assembly). Si dejamos una clase sin sellar, debemos ser conscientes de que estamos dando la posibilidad a alguien de que derive de nuestra clase. Eso, obviamente, no tiene nada de malo… pero entonces debemos asegurarnos de que nuestra clase está preparada para que se derive de ella.

Métodos virtuales

Los métodos virtuales definen los puntos de extensión de nuestra clase: es decir la clase derivada sólo puede redefinir (override) los métodos que nosotros hayamos marcado como virtuales en nuestra clase. Ese es uno de los aspectos que más me gustan de C# respecto a Java: en Java no hay el concepto de método virtual (o dicho de otro modo, todos lo son). En C# (y en VB.NET) al tener que marcar explícitamente los métodos que vamos a permitir que las clases derivadas redefinan, nos obliga a tener que pensar en cómo puede extenderse nuestra clase. Si nuestra clase no tiene ningún método virtual… qué sentido tiene dejarla sin sellar? Si nuestra clase no tiene métodos virtuales es que o bien hemos pensado que no tiene sentido que se extienda de ella, o que ni hemos pensado en cómo puede extenderse, y en ambos casos, para evitar problemas, es mejor dejar la clase sellada.

Miembros privados

Los miembros privados sólo son accesibles dentro de la propia clase que los declara. Cuando creamos una clase pensada para que se pueda heredar de ella, debemos tener siempre presente el principio de sustitución de Liskov (LSP). Este principio es, digamos, la base teórica del polimorfismo, y viene a decir que si S es una clase derivada de T, entonces los objetos de tipo T pueden ser reemplazados por objetos de tipo S sin alterar el comportamiento de nuestro sistema.

Si estáis habituados con el polimorfismo, diréis que viene a ser lo mismo… pero de hecho es posible tener polimorfismo sin respetar LSP. El polimorfismo viene dado por el lenguaje: es el lenguaje quien nos deja usar objetos de tipo S donde se esperen objetos de tipo T, pero el lenguaje no nos garantiza que se respete el LSP… eso debemos hacerlo nosotros.

¿Y que tiene que ver ese rollo con los métodos privados? Pues bien… imaginad un método virtual (por lo tanto redefinible desde la clase base), que accede a un método privado, para comprobar por ejemplo una precondición:

public class T {
private int count;
public virtual void Remove() {
if (count <= 0) throw new InvalidOperationException();
}
}

Si alguien deriva de nuestra clase T, y redefine el método Remove no tiene mecanismo para poder comprobar esa precondición. Es decir, incluso aunque nosotros documentemos la precondición quien redefine el método Remove() no tiene manera de poder reproducirla, puesto que no puede acceder a la variable count.

Así, si quieres que quien herede de tus clases pueda respetar el LSP, recuerda de no acceder a miembros privados desde métodos virtuales.

Excepciones

El LSP implica que los métodos redefinidos en una clase derivada, no deben lanzar nuevos tipos de excepciones que los que lanza el mismo método en la clase base (a no ser que esos nuevos tipos de excepciones sean a la vez subtipos de las excepciones lanzadas en el método de la clase base). Es decir, si un método foo() de una clase base T, lanza la excepcion IOException y se deriva de dicha clase T, al redefinir el método foo puede lanzarse la excepción IOException o cualquier derivada de esta, pero no podría lanzar una excepción de tipo ArgumentException p.ej.

Java define la clausula throws que indica que tipo de excepciones puede lanzar un método y no permite que los métodos redefinidos lancen excepciones de cualquier otro tipo al declarado en throws. C# no tiene ningún mecanismo que pueda obligar al cumplimiento de esta norma del LSP, así que sólo nos queda, al menos, documentar las excepciones que lanza cada método. Otra opción es definir métodos protegidos para lanzar todas las excepciones. De esa manera si quien deriva de nuestra clase detecta que debe lanzar la excepción X, puede llamar al método ThrowX. Eso garantiza que todas las excepciones se lanzan de forma coherente.

Code Contracts

Los que seguís mi blog sabréis que he hablado de Code Contracts un par de veces. Si eres de los que piensa que Code Contracts es un nuevo Debug.Assert, cuando quieras quedamos para tomar unas cervecillas y discutimos el asunto 🙂

Para el tema que nos ocupa, Code Contracts es básicamente una bendición. LSP obliga a que una clase derivada:

  • Debe mantener todas las precondiciones de la clase base, sin poder añadir más precondiciones.
  • Debe garantizar todas las postcondiciones de la clase base, sin poder eliminar postcondiciones.
  • Debe preservar todos los invariantes de la clase base.

Si no usamos Code Contracts, el principal problema es que las precondiciones, postcondiciones y invariantes, son codigo normal. Si en mi método virtual Foo() tengo un código que comprueba una precondición determinada, cuando se redefina este método dicho código debe ser escrito otra vez, para volver a comprobar la precondición si queremos mantener el LSP. Code Contracts nos garantiza esto automáticamente:

class T
{
protected bool empty;
public T()
{
empty = true;
}
public virtual void Add()
{
Contract.Requires(empty);
Contract.Ensures(!empty);
empty = false;
}
}

class S : T
{
public override void Add()
{
}
}

En este código cuando llamamos al método Add() de la clase S, se evalúan las precondiciones del método… que están definidas en la clase base.

De esta manera el desarrollador de la clase S, no debe preocuparse de reimplementar todas las precondiciones y postcondiciones de nuevo y puede concentrarse en lo que interesa: la redefinición del método Add().

Nota Técnica: Usar Code Contracts no nos exime de declara la variable empty con la suficiente visibilidad. Es decir, aunque sólo accedamos a empty dentro de la precondición contenida en T.Add(), debemos tener presente que desde el punto de vista de Code Contracts, esta precondición también se ejecutará dentro del método S.Add(). Y eso en Code Contracts es literal: Code Contracts no funciona creando un método “oculto” en la clase T que compruebe las precondiciones, sinó que modifica el IL generado por el compilador, para “copiar y pegar” las precondiciones y postcondiciones en cada método donde sean requeridas. Así, pues es “literalmente” como si las llamadas a Contract estuviesen también en S.Add(). Si declaramos la variable empty como private, el código compila (puesto que para el compilador todo es correcto), pero al ejecutarse se lanzará una excepción indicandonos que desde S.Add() estamos intentando acceder a un miembro sobre el cual no tenemos visibilidad.

Code Contracts no obliga al cumplimiento estricto de LSP, dado que el desarrollador de la clase S puede añadir nuevas precondiciones al método Add:

public override void Add()
{
Contract.Requires(otherPostcondition);
}

De todos modos si el desarrollador de la clase derivada hace esto, ya lo hace bajo su conocimiento y responsabilidad y además Code Contracts emite un warning para que quede claro: Method ‘CC1.S.Add’ overrides ‘CC1.T.Add’, thus cannot add Requires.

Conclusión

Cuando creamos clases, espcialmente clases públicas que formen parte de una API que usen otras personas, debemos tener especial cuidado a la hora de diseñarlas. Debemos prestar especial atención al LSP y tener presente que aunqué cumplir el LSP (aunqué siempre es muy recomendable) no sea siempre obligatorio, sí que puede serlo en otros casos, y nosotros como creadores de la clase base, debemos asegurarnos de tener el cuidado necesario y facilitar la vida al máximo para que quien derive de nuestras clases pueda cumplir el LSP… Y a riesgo de hacerme pesado, insisto que Code Contracts es una bendición.

Un saludo a todos!

[VS2008] Visual 2008 casca al adjuntarse a un proceso

Post rapidito… Me encontré que VS2008 me rebentaba al adjuntarme a un proceso para depurar. Aunque salía el mensaje de “Visual Studio ha causado un error y debe cerrarse”, dándole a OK, Visual Studio no se cerraba y realmente se adjuntaba al proceso… eso sí, todas las ventanas se iban a la posición que les daba la gana… lo cual da bastante por el saco si tienes que ir poniéndolas cada una a su sitio de nuevo…

En fin, si te ocurre esto o bien VS2008 te da errores cuando mueves y/o desacoplas ventanas, descárgate el patch que se encuentra en http://code.msdn.microsoft.com/KB960075 y listos.

Ah, por cierto, y si no te ha pasado nunca no te confíes… yo hasta hoy no lo había necesitado… En fin…

Saludos!

Mi modesta comparativa entre PURE y jQuery-tmpl

Hola! En mis dos últimos posts he estado hablando un poco de PURE, una herramienta para generar código HTML a partir de una plantilla y un objeto Json. Hace poco Microsoft ha presentado jquery-tmpl, su propuesta (todavía abierta y en fase de discusión) para realizar exactamente lo mismo: generar HTML a partir de plantillas y json. Más detalles los podeis encontrar en este post de Stephen Walher.

Ni soy (ni me considero) un experto ni en PURE ni en javascript, pero me he permitido realizar una pequeña comparativa entre PURE y la propuesta de microsoft para templates, para ver en que se parecen y en que se diferencian. Esta es mi comparativa, sin duda limitada por mi propia falta de conocimientos, pero que comparto con vosotros por si os parece de interés.

Al final del post encontraréis en enlace con el código fuente del proyecto, que en este caso es un lector de los feeds de geeks.ms, implementado en ASP.NET MVC. Básicamente hay dos vistas: una que se genera usando PURE y otra usando jquery-tmpl.

No voy a comentar nada de la parte “servidor” del proyecto, puesto que teneis el códgo fuente en el zip (y obviamente si alguien tiene alguna duda puede contactar conmigo). Lo que quiero comentar es el código de generación usando PURE y jquery-tmpl.

1. Generación usando PURE

La generación usando PURE no tiene mucho secreto. La plantilla está definida de la siguiente manera:

<div id="pure">
<h1>
</h1>
<h2>
</h2>
<span class="t1" id="generator"></span>
<p class="feed">
<a></a>
<br />
<span></span>
</p>
</div>

Toda la vista está generada usando PURE, puesto que el objeto JSON tiene toda la información necesaria.

El código para generar el template, también es sencillo:

var directive = {
'h1': 'Channel.Title',
'h2': 'Channel.Description',
'span#generator': 'Powered by: #{Channel.Generator}',
'p.feed': {
'feed<-Channel.Items': {
'a': 'feed.Title',
'a@href': 'feed.Link',
'span': function(ctx) {
return stripped = ctx.item.Description.replace(/(<([^>]+)>)/ig, "").
substring(0, 350) + "...";
}
}
}
};

$("#pure").render(data, directive);

Como casi siempre, se declara una directiva que controlará el renderizado y finalmente se llama al método render.

Si echamos un vistazo a la directiva vemos una cosa que no había mostrado en los posts anteriores, y es la posibilidad de llamar a métodos javascript: en este caso en el span se colocará el resultado que devuelva el método anónimo declarado en la directiva. Cuando se llama a una función javascript desde un bucle, PURE le pasa un objeto que tiene, entre otras, la propiedad item que es el objeto correspondiente a la iteración que se está renderizando. El método es sencillo: lo que hace es eliminar el código HTML del feed y truncarlo a 350 carácteres.

2. Generación usando jquery-tmpl

Antes que me olvide: jquery-tmpl requiere jquery 1.4.2. Con la versión 1.4.1 (que es la que usa el generador de proyectos de ASP.NET MVC 2 RC2) no funciona.

A diferencia de PURE, jquery-tmpl opta por tener los templates dentro de un tag <script> cuyo type sea text/html. Este no es un type correcto para un tag <script> por lo que es ignorado por el naveagador. Eso hace que, a diferencia de PURE, los elementos que forman el template no pertenecen al DOM del documento, puesto que son ignorados. Podréis ver la diferencia ejecutando el proyecto y consultando primero los datos con PURE y luego con jquery-tmpl. En el primer caso vereis como el template, a pesar de ser elementos vacíos, es visible ya que tienen estilos de colores y borders. Esto tampoco es que represente más problema: generalmente usando PURE el template está oculto inicialmente y se muestra cuando ha terminado la generación (p.ej. usando el método toggle() de jQuery). Yo no le hecho adrede, para que veáis el efecto.

La gran diferencia de jquery-tmpl respecto PURE, es que no existe el concepto de directiva: el propio template contiene la directiva embebida en su interior. Para ello Microsoft ha recurrido a una sintaxis que nos resulte lo más familiar posible. La definición del template queda así:

<script id="template" type="text/html">
<h1>{%= Channel.Title %}</h1>
<h2>{%= Channel.Description %}</h2>
<span class="t1" id="generator">{%= Channel.Generator %}</span>
{% for (var i=0, l = Channel.Items.length; i<l; i++) { %}
<p class="feed">
<a href="{%= Channel.Items[i].Link %}">
{%= Channel.Items[i].Title %}
</a>
<br />
<span>{%= strip(Channel.Items[i].Description) %}</span></p>
{% } %}
</script>

Se puede observar el uso de la sintaxis {% y %}, que recuerda a la <% y %> usada en páginas aspx para el código de servidor. De esta manera {%= expr %} se traduce por el valor de expr donde expr se evalúa en el contexto del objeto json. Así {%= Channel.Description %} se traduce por el valor de la propiedad Description, de la propiedad Channel del objeto json.

También vemos el uso de {% y %} para iterar: Las etiquetas {% y %} nos permiten colocar código javasacript en nuestro template, en este caso un bucle for para iterar sobre todos los elementos de la colección Channel.Items.

Finalmente fijaos en la llamada a la función strip dentro del <span>. La función strip sirve para eliminar el código html del feed y truncarlo a 350 carácteres, y está definida dentro de un tag <script> en la propia página:

function strip(str) {
return str.replace(/(<([^>]+)>)/ig, "").substring(0, 350) + "...";
}

Hemos visto como definimos el template… y como lo aplicamos? Pues muy fácil, primeramente necesitamos el contenedor (o sea, el sitio donde se va a colocar el código HTML generado):

<div id="mstemplate">
</div>

Y una vez tenemos los datos en json, seleccionamos el template (usando un selector de jquery) y llamamos a su método render, pasándole los datos json y finalmente llamamos a appendTo para que el resultado se coloque dentro del contenedor que indiquemos.

$("#template").render(data).appendTo("#mstemplate");

3. Algunas conclusiones mías

La principal diferencia es que en PURE el template es un conjunto de objetos DOM conocidos por el navegador, mientras que en jquery-tmpl, el template es ignorado por el navegador ya que está dentro de un <script> cuyo tipo es “text/html”. En el apartado de discusiones del documento de propuesta de microsoft, se comenta el hecho que los templates no son objetos DOM para evitar efectos colaterales. Esto es cierto si se adopta la filosofía de que el template tenga la directiva embebida. Si mi template tiene algo como:

<img src="{%= ImageUrl %}" />

Si el template fuese un objeto DOM reconocido por el navegador, éste intentaría cargar la imagen “{%= ImagenUrl %}”, la primera vez. Esto es algo que NO podemos evitar haciendo que el template sea invisible. Es por ello que Microsoft opta por poner el template dentro de un tag <script> cuyo type sea “text/html” y de esta manera sea ignorado por el navegador.

En PURE no hay este problema, ya que la directiva está separada y además PURE puede crear atributos que no estén en el template si la directiva así lo indica. De este modo, el template para el caso anterior en PURE queda como:

<img />

Y la directiva asociada es la que crea el tag src dentro del tag <img>:

var directive = { 'img@src' : 'ImageUrl'};

De este modo el template puede ser un objeto DOM sin efectos colaterales.

No sé si el hecho de que los templates sean objetos DOM reales o no, tiene mucha importancia. Lo que si me hizo notar @tchvil es que con PURE la página sigue siendo xhtml, mientras que con jquery-tmpl no, por el uso de la sintaxis {% … %} y que eso puede tener su importancia en según que casos.

4. ¿Es necesario un estándard de templates en jquery?

Si no he entendido mal, la intención de microsoft con jquery-tmpl (que recuerdo está en fase de definición) es que se convierta en el método estándard de templates en jquery. Yo no se si es necesario que haya un mecanismo estándard de templates en jquery. Pienso que es bueno que el core sea lo más pequeño posible y que se use el mecanismo de plugins para ir enchufando las distintas funcionalidades. De este modo cualquiera podrá usar el mecanismo de templates que más le convenga en cada momento. Aunque en el documento se justifica la inclusión de un mecanismo estándard de templates en el core de jquery para que así si alguien quiere desarrollar un plug-in para jquery pueda usar templates sabiendo que estarán soportados…

Lo que sí me ha parecido ir leyendo en algunos posts, es que Microsoft va a ir abandonando su Ajax Library (me refiero a la parte de cliente, no a los controles ajax de asp.net) para ir centrando esfuerzos en jquery. Si realmente es así me parece una decisión excelente y que apoyo plenamente.

Como comenté al principio aquí tenéis el código del proyecto para que hagáis con él lo que queráis! (Está en mi skydrive).

Un saludo!

Lector de Twitter con ASP.NET MVC y Pure

Hola a todos! El otro día os comentaba lo mucho que me está gustando pure… Hoy, a modo de ejemplo, os presento como realizar fácilmente un lector de Twitter que muestre los últimos tweets…

El proyecto asp.net mvc

No voy a mencionar mucha cosa del proyecto asp.net mvc en general puesto que al final hay un enlace con todo el código. Consta de dos controladores:

  1. Home: Que tiene las acciones Index, Login y Tweets. La acción Index redirige a Login o bien a Tweets en función de si el usuario ha entrado o no login y password de twitter. La acción Login muestra un formulario de login y redirige a la acción tweets, que es la que hace el trabajo.
  2. La acción Tweets se limita a mostrar una vista que es la que hace gran parte del trabajo.

1. La vista Home/Tweets

Esta vista, hace una llamada ajax a la acción List del controlador Tweeter (nuestro segundo controlador). Esta acción debe devolver la información json con los últimos tweets del usuario. El código, es gracias a jquery, trivial:

$(document).ready(function() {
$.getJSON (
"<%= Url.Action("List","Twitter") %>",
function(val)
{
renderPure(val);
});
});

El método renderPure será el que usara Pure para mostrar los datos.

2. El controlador Twitter

Twitter dispone de una fantástica API REST con la cual podemos hacer casi cualquier cosa. Esta API está preparada para devolver datos usando distintos formatos, entre ellos json.

Algunas de las funciones de dicha API son públicas, pero otras (como la  de ver los últimos tweets) requieren que nos autentiquemos en twitter. Hay dos formas de hacerlo: usar OAuth o bien basic authentication.

OAuth es un protocolo de autenticación, pensado para integrar distintas aplicaciones, y una de sus ventajas es que el usuario no debe dar su contraseña en ningún momento, además de que puede granularizar sus permisos (puede dar permisos de lectura pero no de escritura p.ej.). Es sin duda el método recomendado para integrarnos con Twitter. Si usais cualquier cliente Twitter (web o escritorio) que no os haya pedido el password y que además hayais tenido que darle permisos desde la web de twitter, entonces este cliente está usando OAuth.

Basic authentication por otro lado es muy simple: se basa en poner una cabecera en la petición http, con nombre Authorization y cuyo valor sea Basic login:password, donde la cadena “login:password” está en Base64. Es un método totalmente inseguro (salvo que se uso junto con SSL) y además el usuario debe proporcionar su usuario y su contraseña al cliente (lo que bueno… mucha confianza no genera). Pero es muy sencillo de implementar, y es el que yo he usado.

El controlador Twitter tiene una sola acción List que realiza una llamada a la API REST de Twitter y pasa el resultado (json) tal cual:

public ActionResult List()
{
UserModel um = this.Session["user"] as UserModel;
string url = "http://api.twitter.com/1/statuses/friends_timeline.json";

// Realiza la petición a twitter
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Headers.Add("Authorization", string.Format("Basic {0}", um.Base64Data));
req.Method = "GET";
HttpWebResponse res = (HttpWebResponse)req.GetResponse();

string data = null;
if (res.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(res.GetResponseStream()))
{
data = sr.ReadToEnd();
}
}

return !string.IsNullOrEmpty(data) ? (ActionResult)this.RawJson(data) : (ActionResult)View("Error");
}

Fijaos que el código es muy simple: Recojemos un objeto UserModel de la sesión. Ese objeto contiene el login y password de twitter del usuario. Con esos datos construimos una petición a la URL de la API REST de Twitter y añadimos la cabecera Authorization. Finalmente leemos el valor devuelto (una cadena json, salvo error) y lo mandamos tal cual.

El método RawJson es un método mío de extensión que devuelve un objeto RawJsonActionResult:

public static RawJsonActionResult RawJson(this Controller @this, string data)
{
return new RawJsonActionResult() { RawData = data };
}

La clase RawJsonActionResult es un ActionResult mío, que me permite devolver una cadena json. No uso el JsonResult de MVC porque ese está pensado para construir una cadena json a partir de un objeto .NET, y yo ya tengo la cadena json. Como podeis ver, el código es trivial:

public class RawJsonActionResult : ActionResult
{
public string RawData { get; set; }

public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "application/json";
response.Write(RawData);
response.Flush();
}
}

Quizá alguien se pregunta porque hago una llamada a un controlador mío (Twitter) que lo único que hace es consultar a la API REST de Twitter y mandarme el mismo resultado (sin tratar). La respuesta es para evitar problemas de cross-domain: de esta manera desde la vista Home/Tweets la única llamada ajax que hay es una llamada a otro controlador del mismo dominio, lo que seguro que no me va a generar ningún problema.

3. La vista Home/Tweets (ii)

Una vez la vista Home/Tweets recibe el objeto json (resultado de la llamada a la acción List del controlador Twitter), debemos generar el html necesario para “dibujarlo”… Aquí entra en acción PURE.

El objeto json que nos devuelve twitter es bastante grandote (aunque gracias a firebug se puede ver muy fácilmente). Por cada tweet yo voy a mostrar:

  1. El avatar de quien ha hecho el tweet
  2. El texto del tweet
  3. El nombre de usuario y el nombre real

Para ello usaré la siguiente plantilla:

<div id="purecontent" style="display:none">
<div class="tweet">
<img class="picprofile"></img>
<p>
<span id="text"></span>
</p>
<div class="userdata"><span id="user"></span> (<span id="realname"></span>)</div>
</div>
</div>

Dentro del div “purecontent” se generaran tantos div “tweet” como tweets haya… Veamos el código del método renderPure:

function renderPure(data)
{
var directive = {
'div.tweet' : {
'tweet<-': {
'span#text' : 'tweet.text',
'span#text@id+' : 'tweet.id',
'span#user' : 'tweet.user.screen_name',
'span#user@id+' : 'tweet.id',
'span#realname' : 'tweet.user.name',
'span#realname@id+' : 'tweet.id',
'img.picprofile@src' : 'tweet.user.profile_image_url'
}
}
};
$("#purecontent").render(data, directive);
$("#purecontent").toggle();
}

Lo más importante (como no) es la directiva, que le indica a pure como renderizar los datos a partir de la plantilla y el objeto json. En concreto esa plantilla dice lo siguiente:

  1. Por cada elemento del elemento json (tweet<-) haz lo siguiente:
    1. En el span cuyo id es text (span#text) pon el valor de la propiedad text del elemento.
    2. En este mismo span modifica su id para que el nuevo valor sea el id previo (text) más el valor de la propiedad Id del elemento.
    3. Haz lo mismo con los spans user y realname, pero usando las propiedades screen_name y name del objeto user que está dentro del elemento.
    4. Finalmente en el atributo src de la img cuya clase es picprofile (img.picprofile@src) coloca el valor de la propiedad profile_image_url del objeto user que está dentro del elemento.

 

Que… fácil, eh? Al principio igual cuesta acostumbrarse pero una vez se le pilla el truquillo es muy potente y natural.

Finalmente llamamos al método render de PURE y por último hacemos visible el div padre (purecontent) mediante el método toggle de jQuery…

Y este es el resultado (por cierto José M. Aguilar… ponte ya un avatar :p)

image

Os dejo un zip con el proyecto entero para que le echeis un vistazo.

Saludos!!!