martes, 21 de diciembre de 2010

Opinión: Var o no var… esa es la cuestión.

Hola a todos! Desde hace algunos días estoy usando Resharper. La verdad no era, como decirlo, muy proclive para instalármelo, ya que había tenido no muy buenas experiencas con CodeRush. Seguramente no eran culpa de CodeRush sinó mías, pero bueno… Al final me lo instalé y debo decir que estoy gratamente sorprendido: Es una auténtica maravilla.

Una cosa interesante de Resharper es que te hace sugerencias (que puedes desactivar si quieres, por supuesto) sobre como codificar mejor. Y una de las sugerencias es usar var siempre que se pueda:

image

Fijaos que incluso en un caso trivial como int i=0; nos recomienda que usemos var.

Nota: Primero una pequeña aclaración sobre var: Por si acaso… Recordad que var no es tipado dinámico, ni late-binding ni nada parecido a esto. Var simplemente le indica al compilador que infiera él el tipo de la variable. Pero la variable tiene un tipo concreto y lo tiene en tiempo de compilación. Por lo tanto olvidad todos vuestros prejuicios (si los tenéis) sobre tipos dinámicos.

Hay tres corrientes de opinión al respecto de cuando usar var: Hay gente que opina que debe usarse sólo cuando es necesario (cuando se trabaja con objetos anónimos).

Otros opinan que cuando el tipo ya aparece en la lína de código puede usarse var. Es decir, admiten esto:

var dict = new Dictionary<int, string>();





Porque el tipo de la variable ya aparece en el new. Pero no admiten lo siguiente:




var result = stockManager.GetStocks();





Porque, viendo el código: como se puede saber el tipo de result? (Debes irte a ver que devuelve el método GetStocks).



Por último el tercer grupo de opinión está a favor de usar var siempre. Incluso en los casos más triviales.



Por curiosidad:




  1. La msdn se situa en el primer grupo de opinión (literalmente dice “the use of var does have at least the potential to make your code more difficult to understand for other developers. For that reason, the C# documentation generally uses var only when it is required” - http://msdn.microsoft.com/en-us/library/bb384061.aspx).


  2. Resharper se sitúa en el tercer grupo, como hemos visto


  3. Yo me situaba en el segundo grupo de opinión.



¿Que aporta usar siempre var?



Que Resharper me estuviese continuamente insistiendo en usar var me hizo pensar en el por que de esa razón. Así que lo que hice fue probar y hacerle caso. Y empecé a usar var en todos los sitios. A ver que ocurría.



Y ocurrió una cosa interesante…



Al usar var continuamente pierdes el tipo de la variable de forma visual (es decir no sabes de que tipo es sólo viendo esa línea de código) y entonces te das cuenta de una cosa: que muchos nombres de variables aportan muy poca información sobre que hace realmente la variable. Los detractores de var dicen que puede complicar la lectura de código… pero que es más dificil de entender, esto:




Player plr = GetCurrentPlayer();
Location l = plr.GetCustomLocation();
// Varias líneas de código hablando de "l" y "plr"





o esto:




var player = GetCurrentPlayer();
var location = player.GetCustomLocation();
// Varias líneas de código hablando de "location" y "player"





El tema está en que la variable la declaras en una línea, y la usas en varias más. Evidentemente, si no usas var, la línea que declara la variable te da más información (exactamente el tipo de la variable), información que pierdes si usas var. Pero, como te das cuenta que estás perdiendo esta información, lo que haces es usar lo que resta de la línea para aumentar la claridad. Y que es lo que queda? El nombre de la variable.



Al usar var en todos los sitios te fuerzas a poner nombres más declarativos a tus variables, que expresen lo que la variable hace. Y todos sabemos que esa es una muy buena práctica. Y a veces no la seguimos, y una de las razones es que al tener una línea tipo:




Location l = new Location();





Mentalmente piensas: <<Ya se ve que “l” es una Location. Ya queda claro.>>



Pero no es cierto, porque al cabo de unas cuantas líneas te aparece una “l” y tienes que recordad que era una Location.



Mientras que si usas var, cuando escribes la línea tiendes a usar nombres más descriptivos, porque escribir:




var l = new Location();





Hace como daño a la vista :)



Así que la sugerencia de usar var siempre, personalmente no me parece muy desacertada, y creo que voy a tomar esa opción de ahora en adelante.



Pero… Y vosotros? ¿Que opináis al respecto?



Un saludo!



PD:  También creo que hay otra razón para que Resharper nos guie a usar siempre var y es que el código con var es más fácil de refactorizar (menos cambios) que el que usa declaraciones explícitas.



PD2: Como no… otro crosspost desde mi blog de geeks.ms!

jueves, 16 de diciembre de 2010

ASP.NET: Obtener el ID del usuario actual

Buenas!

No se vosotros, pero yo cuando desarrollo mis aplicaciones, si uso FKs de la otabla de usuarios, las hago en base al ID del usuario, nunca en base a su nombre. Así pues, saber el ID del usuario actualmente autenticado en mi aplicación es algo fundamental.

Primero, para saber el nombre del usuario autenticado podemos usar:

string userName = HttpContext.Current.User.Identity.Name;







El tema está en que las mentes pensantes que parieron el sistema de proveedores de autenticación en ASP.NET no tuvieron a bien a poner un campo para guardar el ID.



Por suerte obtenerlo es trivial:




int i = (int)Membership.GetUser().ProviderUserKey;





Ojo con ese código: Yo uso un membership provider propio, ya que mi base de datos usa ints para los IDs de usuarios. Si usáis el membership provider que viene por defecto en ASP.NET, el cast lo debéis hacer a Guid y no int.



Bueno todo muy bonito, pero antes que descorchéis el cava: eso hace una llamada a la base de datos. Además se trae todos los campos del registro correspondiente de la tabla de usuarios (que si usáis el membership provider que viene por defecto tiene la hostia y pico). O sea que cuidado con usar eso a mansalva… :)



Eeeerrr… ¿se puede SIN necesidad de acceder a la base de datos?



Bueno… esa es la gran pregunta, no nos vamos a engañar ;-) Hay varias maneras de poder acceder al ID del usuario sin hacer un round-trip a la base de datos, pero a si a bote pronto se me ocurren dos:




  1. Guardarlo en una variable de sesión: Podemos guardar el ID en una variable de sesión y consultarla cuando la necesitamos. Para una escalabilidad máxima podéis no usar sticky sessions y en el Session_Start guardar dicha variable con el ID. Si no usáis sticky sessions un mismo usuario puede iniciar sesión en varios IIS a la vez, pero en nuestro caso no es problemático (simplemente se consultará el ID del usuario cada vez que inicie sesión). Eso sí, estoy asumiendo que no guardáis nada más en la sesión (es decir que funcionalmente no dependéis de la sesión).


  2. Guardarlo en la cookie de autenticación del usuario. Esto no es, ni de lejos tan sencillo como el punto anterior, pero ya que hemos llegado hasta aquí…



Modificar la cookie de autenticación



Primero debemos hacer que cuando se cree la cookie de autenticación se añada el ID del usuario. En mi caso, como siempre, uso ASP.NET MVC, así que modificaré el código de AccountController (que es el que genera VS). Para aplicaciones webforms ese código debe colocarse cuando se va a hacer login del usuario.



En el código por defecto que genera VS para autenticar un usuario se usa:




public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");

FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}





Este código está en la clase FormsAuthenticationService (dentro de AccountsModel.cs) y no tiene ningún secreto: lo que hace es crear la cookie de autenticación de ASP.NET.



En nuestro caso vamos a modificar ese código por el siguiente:




public void SignIn(string userName, bool createPersistentCookie)
{
if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
int id = 100; // Aquí va el ID del usuario que pillaríamos de la BBDD
string userData = id.ToString();
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userName, DateTime.Now, DateTime.Now.AddMinutes(30), createPersistentCookie, userData);
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
HttpContext.Current.Response.Cookies.Add(faCookie);
}





Lo que hacemos es crear una cookie, con datos adicionales (el ID del usuario).



Ahora lo que nos toca es la otra parte: reemplazar el valor de HttpContext.Current.User.Identity por uno propio que tenga el ID. Para ello usamos el evento PostAuthenticate_Request:




protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
CustomIdentity identity = new CustomIdentity(authTicket.Name, authTicket.UserData);
GenericPrincipal newUser = new GenericPrincipal(identity, new string[] {});
Context.User = newUser;
}
}





Recogemos la cookie de autenticación, desencriptamos el ticket de autenticación por forms y con los datos (el nombre y el UserData) creamos un objeto de tipo CustomIdentity, clase nuestra que nos implementa IIdentity. Luego la incrustamos dentro de un GenericPrincipal y lo establecemos a la propiedad User del HttpContext.



Nota: El segundo parámetro del constructor de GenericPrincipal es el array de roles a los que pertenece el usuario. En mi caso no uso roles, así que le asigno un array vacío.



La clase CustomIdentity es tal y como sigue:




class CustomIdentity : IIdentity
{

public CustomIdentity(string name, string id)
{
IsAuthenticated = true;
Name = name;
Id = Int32.Parse(id);
AuthenticationType = "Forms";
}

public string AuthenticationType { get; private set; }
public bool IsAuthenticated { get; private set; }
public string Name { get; private set;}
public int Id { get; private set; }
}





De esta manera, ahora podemos al Id del usuario, desde un controlador:




CustomIdentity ci = (CustomIdentity)ControllerContext.HttpContext.User.Identity;
int IdUsuario = ci.Id;





Un misterio con el que me he encontrado es que el código de PostAuthenticateRequest si se pone en AuthenticateRequest (que parece que debería funcionar igual), se queja diciendo que la clase “CustomIdentity” no es serializable. No tengo muy claro porque ocurre eso y eso si que parece ser propio de MVC. Aquí hay más información al respecto: http://stackoverflow.com/questions/1884030/implementing-a-custom-identity-and-iprincipal-in-mvc



Y Listos!



Con esto podemos acceder al ID de nuestros usuarios sin necesidad de usar para nada la base de datos. Además, dado que estamos usando el sistema de autenticación de ASP.NET (no hacemos nada raro), nos siguen funcionando los filtros de autenticación como [Authorize].



Un saludo!



Referencia: http://stackoverflow.com/questions/1064271/asp-net-mvc-set-custom-iidentity-or-iprincipal



PD: Eso es (como siempre) un crosspost desde mi blog en geeks.ms!

martes, 23 de noviembre de 2010

Trick: Enviar datos en JSON usando POST

Muy buenas!

Una de las preguntas que mucha gente se formula cuando empieza a hacer cosillas con ajax y jQuery es ¿Como enviar datos codificados en JSON usando POST?

La verdad es que es muy sencillo, aunque jQuery no proporciona ninguna función por defecto que haga esto. Vamos a ver tres aproximaciones, las dos primeras incorrectas pero que nos acercarán para llegar al final a la una forma correcta de hacerlo.

Aproximación 1: Usando $.post

jQuery tiene una función específica para enviar datos usando post, llamada $.post. Así podriamos pensar que el siguiente método funcionaria:

function JsonPost(data, url, handler)
{
$.post(url, data, handler);
}





Y luego podríamos llamarlo de la siguiente manera:




// Envia un objeto con propiedades Col y Row a /Map/ViewPort y
// llama a fillMap con el resultado devuelto
JsonPost({ Col: c, Row: r}, "/Map/Viewport", function(data) {
fillMap(data);
});





Pero eso no realiza una petición JSON. Si usamos, p.ej. Firebug para analizar la petición vemos que lo que se envía al controlador es:



image



Si os fijáis los datos no están codificados en JSON, sinó en el formato “estándard” (param=value&param=value&…)



Si se lee la documentación de $.post() se ve que acepta un último parámetro datatype, así que podríamos pensar que poniendo dicho parámetro a “json” funcionará, pero no. El parámetro “datatype” indica el tipo de datos esperado de vuelta, no el que se envia.



Resumiendo: $.post() siempre envía los datos en el formato tradicional.



Aproximación 2: Usando $.post() y convertir los datos a Json



De acuerdo, hemos visto que $.post() nos transforma nuestro objeto javascript en la cadena tradicional de param=value&param=value&… Pero si a $.post() se le pasa una cadena la manda tal cual, por lo que si transformamos el objeto javascript a una cadena JSON parece que todo funcionará.



Para transformar un objeto javascript a notación JSON podemos usar el plugin jQuery-Json. Puede haber otros plugins por ahí, pero uséis el que uséis, aseguraros que soporta native json. Native json es una funcionalidad que los nuevos navegadores traen y que se basa en un método llamado JSON.stringify que transforma el objeto pasado a una cadena json. Evidentemente siempre será mucho más rápido si el propio navegador puede hacer esto de forma nativa que si es código javascript que construye la cadena. El plugin jQuery-json usa JSON.stringify si está disponible y sólo en caso de que no exista usa código javascript.



Así pues podríamos modificar nuestra JsonPost para que quede como:




function JsonPost(data, url, handler)
{
$.post(url,$.toJSON(data), handler);
}





El método $.toJSON es el método proporcionado por el plugin. Ahora sí que estamos usando JSON para enviar los datos:



image



Ok. Un parentesis: Recordad que MVC2 no tiene soporte directo para realizar binding de datos enviados en JSON. Aunque es muy fácil crearse un Value Provider propio que de soporte a JSON en MVC2. Y recordad que MVC3 ya viene con el soporte por defecto de JSON.



Bien, si usáis un value provider para JSON que os hayais encontrado por “ahí afuera” lo más probable es que no os funcione, es decir que en el controlador no recibáis los datos. Por que? Pues por lo que está marcado en rojo en la imagen antrior: aunque estamos enviando los datos en formato json, el content-type no es correcto. El content-type de JSON es application/json y este es el content-type que suelen mirar los value providers de JSON.



Aproximación 3: Usando $.ajax()



Bien, ya que $.post() no permite especificar el content-type, la tercera y última aproximación es usar $.ajax(), que es la función más personalizable que tiene jQuery para hacer peticiones ajax.



De hecho básicamente lo único que tenemos que cambiar respecto la aproximación anterior es el content-type:




function JsonPost(data, url, handler)
{
$.ajax(
{
url: url,
type: "POST",
success: handler,
data: $.toJSON(data),
contentType: "application/json"
});
}





Y lo que nos muestra Firebug de la petición:



image



Fijaos que ahora el propio Firebug reconoce que la petición es en JSON y me muestra los datos en JSON.



Espero que os sea útil!



Un saludo!



PD: Como no… otro crosspost desde mi blog en geeks.ms!

viernes, 19 de noviembre de 2010

[Evento] Hasta donde se puede llegar con ASP.NET MVC?

Muy buenas!

El jueves 2 de diciembre voy a realizar un WebCast para la gente del Lleida DotNetClub sobre ASP.NET MVC.

La idea es empezar desde cero a desarrollar una aplicación ASP.NET MVC y ver hasta donde llegamos. Iremos explorando el framework y viendo paso a paso sus características: controladores, vistas, modelos, vistas parciales, ajax, helpers, inyección de dependencias,…

La idea es que sea 100% Visual Studio, nada de powerpoints que de esto ya se encuentra mucho en la web!

La página de registro está en https://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032471331&EventCategory=4&culture=es-ES&CountryCode=ES

Así que si estás interesado en ver ASP.NET MVC, ya sabes: te esperamos! ;-)

Saludos y gracias a la gente del Lleida DotNetClub por invitarme a dar la charla! :)

PD: Huelga decir que si tienes cualquier comentario sobre algo específico que te gustaría ver en este Webcast coméntalo por aquí y voy a ver como se puede encajar! De lo que se trata es que este Webcast sea interesante para vosotros!

PD2: Pues sí… Eso es oooooooootro crosspost de mi blog de geeks.ms

jueves, 18 de noviembre de 2010

C# Básico: Covarianza en genéricos

Muy buenas! Hacía tiempo que no escribía nada de la serie C# Básico. En esta serie voy tratando temas (sin ningún orden en particular) que considero que son fundamentos más o menos elementales del lenguaje. No es un tutorial al uso, cada post es independiente del resto y como digo no están ordenados por nada en particular.

El post de hoy nace a raíz de una pregunta que vi en los foros de msdn (http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/daf808ed-a0aa-4e1e-88ed-64ee60cce918), donde un usuario preguntaba porque el intentar convertir una List<LogVehiculos> a List<Log> le daba error teniendo en cuenta que LogVehiculos derivaba de Log.

Mi respuesta fue que en C# 3.5 los genéricos no son covariantes, y este post es para explicarlo todo un poco más :)

Antes que nada, covarianza y contravarianza son dos palabrejas muy molonas para explicar dos conceptos que son muy básicos pero que tienen implicaciones muy profundas. El mejor artículo en español que he leído sobre covarianza y contravarianza es el del Doctor (maestros hay algunos, doctores muchos menos) Miguel Katrib que salió publicado en la DotNetMania número 62 y titulado “La danza de las varianzas”. Es un artículo que debe leerse con atención pero sin duda de lo mejorcito que he leído nunca. Este post no entrará ni mucho menos en la profundidad de dicho artículo, así que si os interesa el tema, ya sabeis: haceros con dicha DotNetMania.

En este post nos vamos a centrar sólamente en la covarianza.

Covarianza

Llamamos covarianza a algo muy simple: Cuando permitimos sustituir un tipo D por otro tipo B. Para que eso sea posible debe cumplirse una condición: Que no haya nada que pueda hacerse con B y NO pueda hacerse con D.

Vamos a suponer que tenemos una clase Animal, de la cual deriva la clase Perro:

class Animal
{
public void Comer() { ... }
public void Dormir() { ... }
}

class Perro : Animal
{
public void VigilarCasa() { ... }
}





Si tenemos un método cualquiera que devuelva un perro, nosotros podemos convertir el resultado a un animal:




Perro ComprarPerro() { ... }
// entonces eso es válido:
Animal animal = ComprarPerro();





Eso es covarianza: el poder sustituir la clase derivada (Perro) que devuelve el método con la clase base (Animal). C# soporta covarianza entre una clase derivada y su clase base (como hacen de hecho todos los lenguajes orientados a objetos).



Tiene lógica, porque fijaos que no hay nada que pueda hacerse con un Animal (B) que no pueda hacerse con un Perro (D): Dado que Perro deriva de Animal hereda todos sus métodos y propiedades.



Pero la covarianza se da también en más casos y algunos de ellos están soportados en C#. Veamos…



Covarianza en delegados



Se trata de poder asignar a un delegado que devuelve un Animal un método que devuelve un Perro:




delegate Animal AnimalDelegate();
class Program
{
static Perro ObtenerPerro() { return new Perro(); }
static Animal ObtenerAnimal() { return new Animal(); }
static void Main(string[] args)
{
Animal animal = ObtenerPerro();
AnimalDelegate ad = new AnimalDelegate(ObtenerAnimal);
AnimalDelegate ad2 = new AnimalDelegate(ObtenerPerro);
}
}





Fijaos en la segunda declaración (ad2): Aunque el delegate está declarado para métodos que devuelven un Animal podemos usar este delegate con métodos que devuelvan un Perro. Por eso decimos que los delegates son covariantes en C#.



Covarianza en arrays



El siguiente código en C# funciona y es totalmente válido:




Animal[] animales = new Perro[100];





Es decir podemos asignar un array de Perros a un array de Animales. De nuevo los arrays son covariantes en C#. Esta decisión se tomó en su día para, bueno… luego hablaremos más sobre ella :)



Covarianza en genéricos



El siguiente código no compila en C#:




// error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<ConsoleApplication8.Perro>' to 'System.Collections.Generic.List<ConsoleApplication8.Animal>'
List<Animal> animales = new List<Perro>();





Es por ello que decimos que los genéricos NO son covariantes en C#.



Y ahora viene la pregunta… ¿por que?



Bien, recordad que si yo quiero sustituir un tipo D por otro tipo B eso significa que en un objeto de tipo D debo poder hacer cualquier cosa que haga en un objeto de tipo B. Es decir, si hay algo, llamémosle f(), que pueda hacer para un objeto de tipo B que no pueda hacer con un objeto de tipo D, no puedo aplicar covarianza… Ya que entonces podría hacer D.f() que no sería válido (recordad que f() es válido para B y no para D).



Cojamos el caso de List<Animal> y List<Perro> (recordad que Perro deriva de Animal). La pregunta es… hay alqo que podemos hacer con List<Animal> y que NO podamos hacer con List<Perro>? Veamos…




  • Con List<Animal> puedo contar cuantos animales hay. Con List<Perro> también.


  • Con List<Animal> puedo obtener todos los Animales que hay. Con List<Perro> puedo obtener los Perros, pero dado que Perro deriva de Animal, si obtengo un Perro estoy obteniendo un Animal (primer ejemplo que hemos visto). Así pues ningún problema.


  • Con List<Animal> puedo añadir un Animal. Con List<Perro> puedo añadir… un Perro. Ojo que eso es importante: A List<Animal> puedo añadirle cualquier Animal… puede ser un Perro, puede ser un Gato. A List<Perro> no puedo añadirle cualquier animal, debe ser un Perro forzosamente.



Por lo tanto ya hemos encontrado que se puede hacer con List<Animal> que no pueda hacerse con List<Perro>: Añadir un Gato.



Si C# nos dejara aplicar covarianza entonces eso sería válido:




List<Animal> animales = new List<Perro>();
animales.Add(new Gato()); // EEehhh... estoy añadiendo un Gato a una lista de Perros?





Por lo tanto, para evitar eso y asegurar que las listas de perros sólo tendrán perros el compilador no nos deja hacer esa conversión: Los genéricos no son covariantes.



Y los arrays? Recordáis que los arrays sí son covariantes. El siguiente código es válido y legal:




Animal[] animal = new Perro[100];
animal[0] = new Gato(); // Un Gato en una jauría de Perros!





Si ejecutas el siguiente código obtendrás una ArrayTypeMismatchException en tiempo de ejecución. Es decir el código compila pero luego rebienta.



Alguien podría decir que hubiesen aplicado eso mismo a las Listas… dejar que fuesen covariantes y luego rebentar en tiempo de ejecución si añado un Gato a una List<Perro>. Porque no lo han hecho así? Pues porque repetir errores no es nunca una buena solución. Los arrays jamás debieron haber sido covariantes. Si los crearon así fue para dar soporte a lenguajes tipo Java dentro del CLR (Java tiene arrays covariantes). Y así estamos: un error de diseño de Java propagado a .NET. Fijaos que eso obliga a que cada vez que añadimos un elemento en un array el CLR en tiempo de ejecución deba comprobar que el elemento realmente es del tipo del array. Viva la eficiencia!



Y con todo eso… llegó el Framework 4



Bien… Ahora analicemos el siguiente código:




static IEnumerable<Perro> JauriaDePerros()
{
return new List<Perro>();
}
static void Main(string[] args)
{
IEnumerable<Animal> perritos = JauriaDePerros();
}





Ya os lo avanzo: el siguiente código no compila con el Framework 3.5. Recordad: los genéricos no son covariantes y hemos visto la razón. Pero tiene sentido en este caso? Hay algo que pueda hacer con un IEnumerable<Animal> y que no pueda hacer con IEnumerable<Perro>? Veamos…




  1. Con un IEnumerable<Animal> puedo obtener todos los Animales. Con un IEnumerable<Perro> puedo obtener todos los Perros, pero como hemos visto ya, los Perros los puedo ver como Animales.



Y ya está. No puedo hacer nada más con un IEnumerable<> salvo obtener sus elementos. Entonces porque no compila el código en C#? Pues bien, porque pagan justos por pecadores: En el framework 3.5 los genéricos no son covariantes. Nunca, aunque por lógica pudiesen serlo.



Para tener una solución a estos casos donde la covarianza tiene sentido, debemos usar el Framework 4 (VS2010). Una de las novedades que incorpora C# en esta versión es precisamente esta: covarianza de genéricos en según que casos.



Veamos: la covarianza en genéricos es segura cuando el parámetro genérico se usa sólamente de salida. Es decir cuando ningún método acepta ningún parámetro del tipo genérico, como mucho sólo lo devuelven. El problema en el caso de List<> estaba en que podía añadir un Gato a una lista de Perros. Y eso es posible porque uno de los métodos de la clase List<T> es Add(T item). Es decir el tipo genérico se usa como valor de entrada a los métodos. En cambio con IEnumerable<T> hemos visto que no hay ningún problema: En un IEnumerable<T> sólo puedo obtener sus elementos, pero no puedo añadirle elementos nuevos. No hay ningún método que reciba un parámetro del tipo genérico. Como mucho hay métodos que devuelven ojetos del tipo genérico. En este caso la covarianza es segura.



Para indicar en C# 4.0 que una clase genérica es covariante respecto a su tipo genérico, usamos la palabra clave out. P.ej. IEnumerable<T> en C# 4.0 está definido como:




public interface IEnumerable<out T> : IEnumerable
{
// Métodos...
}





Fijaos en el uso de out para indicarle al compilador: Este tipo es covariante respecto al tipo genérico T. Entonces este código que en VS2008 no compilaba, es válido en C# 4.0:




static IEnumerable<Perro> JauriaDePerros()
{
return new List<Perro>();
}
static void Main(string[] args)
{
IEnumerable<Animal> perritos = JauriaDePerros();
}





Por supuesto, esto sigue sin compilar:




static List<Perro> JauriaDePerros()
{
return new List<Perro>();
}
static void Main(string[] args)
{
List<Animal> perritos = JauriaDePerros();
}





Ya que List<T> no es covariante respecto al tipo genérico T (lógico, si lo fuese podría añadir un Gato a una lista de Perros).



En cambio eso si que es correcto en VS2010:




static List<Perro> JauriaDePerros()
{
return new List<Perro>();
}
static void Main(string[] args)
{
IEnumerable<Animal> perritos = JauriaDePerros();
}





Aunque el método JauriaDePerros() devuelve una List<Perro>, el código funciona porque:




  1. List<T> implementa IEnumerable<T>


  2. IEnumerable<T> es covariante respecto a T



En el fondo, fijaos que no hay problema: con perritos lo único que puede hacerse es obtener sus elementos, así que de nuevo no hay peligro de que añada un Gato a perritos.



Declaración de mis clases genéricas covariantes



Si yo creo una clase que quiera que sea covariante con su tipo genérico, simplemente debo usar out. La única restricción es que ningún método de mi clase podrá aceptar un parámetro del tipo genérico:




interface Foo<out T>
{
T Bar() { ... }
void Baz(T t) { ... }
}





Este código no compila (error CS1961: Invalid variance: The type parameter 'T' must be contravariantly valid on 'ConsoleApplication5.Foo<T>.Baz(T)'. 'T' is covariant.). Ese mensaje de error largote lo único que quiere decir es que T es covariante, y por lo tanto no podemos aceptar parámetros de tipo T.



Finalmente tened presente que sólo las interfaces pueden declarar que su tipo genérico es covariante (las clases no).



Bueno… dejémoslo aquí. Hay otro termino ligado a la covarianza que es la contravarianza, aunque no es tan común como la covarianza y quizá algún día hablemos de ella :)



Un saludo y recordaros lo que digo siempre en los posts de esta serie: Si tenéis temas sobre el lenguaje C# que queráis tratar, hacédmelo saber y haré lo que pueda!!!



PD: Para variar, eso es un crosspost desde mi blog en geeks.ms! Pásate por allí que somos más!

viernes, 12 de noviembre de 2010

Saca tus scripts de tu código HTML

Buenas! En el post anterior os comenté el soporte de Unobtrusive Ajax en ASP.NET MVC3. Hoy quiero mostraros que esa técnica ni es exclusiva de MVC3, ni  requiere HTML5 para nada. En fin, que podéis empezar a usarla ya, con independencia de la tecnología que uséis. Lo que contaré en este artículo no es nada “revolucionario” ni una “técnica nueva”…

De hecho, el ejemplo va a ser una página HTML, nada de ASP.NET :)

Veamos, la técnica de Unobtrusive Javascript, se refiere a no tener mezclado código javascript con código de marcado HTML. Es decir, no queremos algo como:

<input type="text" id="txtName" onkeypress="checkKey();" />





Aquí estamos mezclando código HTML con el código javascript (la llamada checkKey en el onkeypress).



Imaginemos que queremos que nuestros textboxes sólo acepten números. Y recordad que el objetivo es no tener código javascript mezclado con nuestro código HTML.



Eso lo podemos conseguir fácilmente, ya con jQuery:




<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Demo Unobtrusive Javascript</title>
<script src="jquery-1.4.1.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function () {
$('input:text').keypress(function (event) {
if (event.keyCode < 47 || event.keyCode > 58) {
event.preventDefault();
}
});
});
</script>

Introduce sólo números: <br />
<input type="text" />
</body>
</html>





Incluso, si no queréis que haya el tag <script> con todo el código, podemos moverlo a un .js separado y usarlo desde nuestra página HTML que entonces quedaría como:




<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Demo Unobtrusive Javascript</title>
<script src="jquery-1.4.1.js" type="text/javascript"></script>
<script src="myscript.js" type="text/javascript"></script>
</head>
<body>
Introduce sólo números: <br />
<input type="text" />
</body>
</html>





Por lo tanto vemos que con jQuery es muy fácil asignar comportamiento a objetos DOM, sin necesidad de andar con los handlers onXXXX.



Ahora bien, el código jQuery selecciona todos los <input type=”text”>, que passa si sólo quiero seleccionar algunos? Como le indico a mi código jQuery que sólo algunos textboxes son numéricos?



Una solución es invertarnos un atributo que indique que elementos queremos como numéricos. De esta manera p.ej. la página HTML queda como:




<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Demo Unobtrusive Javascript</title>
<script src="jquery-1.4.1.js" type="text/javascript"></script>
<script src="myscript.js" type="text/javascript"></script>
</head>
<body>
Introduce sólo números: <br />
<input type="text" datatype="numeric" /> <br />
Aquí puedes introducir lo que quieras: <br />
<input type="text" />
</body>
</html>





Fijaos en el “datatype=”numeric” que es el atributo que me va a servir para decidir que textboxes son numéricos.



Y el código de myscript.js queda como:




$(document).ready(function () {
$('input[datatype=numeric]').keypress(function (event) {
if (event.keyCode < 47 || event.keyCode > 58) {
event.preventDefault();
}
});
});





Y listos, simplemente incluyendo “myscript.js” en cualquier página ya podemos declarar que un textbox es numérico simplemente poniendo el atributo datatype=”numeric”.



Ahora, si alguien hace otra librería javascript para textboxes numéricos si también usa este atributo para indicarlos (ahí está el quid de la cuestión) simplemente cambiando el <script> para que en lugar de ir a myscript.js vaya a la nueva librería, ya tengo todo el cambio hecho… es decir, me he independizado del framework javascript que use.



Y por ahí por donde entra HTML5? Pues bien, como eso de crearnos nuestros propios atributos está bien pero genera HTML que podríamos llamar inválido (en el sentido de que estos atributos no forman parte de HTML), para HTML5 han decidido simplemente que todos estos atributos “inventados” empiecen por data-.



Lo “único” que dice al respecto HTML5 es: “Hey, si tienes que invertarte un atributo para lo que sea, haz que su nombre empiece por data-. Todos los atributos que empiecen por data- son atributos inventados por alquien y deben ser ignorados a todos los efectos (salvo para quien lo haya inventado que hará con él lo que le plazca, claro). Ok, también añade una API específica (element.dataset) para leer esos atributos (pero eso de momento no nos importa ya que no está soportada por la mayoría de navegadores).



Por lo tanto, si en lugar de que mi atributo se llame datatype, hago que le llame data-datatype (p.ej. cualquier nombre que empiece por data-) ya lo tengo todo HTML5 compliant!



De hecho podéis hacer la prueba en http://validator.w3.org/check. Entráis el código HTML de la página y lo validáis contra:




  • HTML5 usando el atributo datatype=”numeric” y os dará error (Attribute not allowed)


  • HTML5 usando el atributo data-datatype=”numeric” y os validará correctamente.


  • Cualquier otra versión de HTML y os dará error en ambos casos.



Y listos! Por lo tanto fijaos que desde ya podeis empezar a aplicar técnicas de “Unobtrusive Javascript”: no necesitáis HTML5 para nada, ni MVC3 ni nada y la recompensa es un HTML mucho más claro y sencillo de ver!



Mi opinión es que, gracias a que HTML5 ha definido un espacio de nombres (data-) para los atrbutos inventados empezaremos a ver, cada ves más, librerías de javascript que usarán esos atributos, y seguramente algunos de ellos terminarán siendo estándares de facto (si yo hago una librería de javascript para validación. pues intentaré usar los mismos atributos data- que use la librería que sea líder en aquel momento, para compatibilizarme con ella).



Por cierto, si vais a usar muchos atributos data- en vuestras páginas web, echadle un vistazo a este plugin de jQuery: HTML5 Dataset.



Un saludo!



Nota: El código de ese artículo lo he probado con IE9 y Firefox 3.6.10.



PD: Esto es un crosspost de mi blog en geeks.ms (como no…)

martes, 9 de noviembre de 2010

Unobtrusive Ajax en MVC3

Buenas! Una de las novedades más interesantes de MVC3 es el soporte para eso que se llama Unobtrusive Ajax. La verdad es que no encuentro una buena traducción para Unobtrusive (discreto no me convence).

La idea del Unobtrusive Ajax es evitar mezclar código script con código HTML. De la misma manera que CSS nos permite separar completamente el código HTML de su representación, con Unobtrusive Ajax vamos a poder separar el código javascript del código HTML.

Pero mejor, veamoslo con un ejemplo, ultra sencillo :)

Imaginad que tengo una vista con este contenido:

<h2>Normal Ajax</h2>
<% using (Ajax.BeginForm("PostData", new AjaxOptions() { HttpMethod="Post", UpdateTargetId="datadiv"})) { %>

<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
<% } %>
<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>





Esta vista genera un <form> con un campo de texto y envía los datos a una acción llamada “PostData” e incrusta el resultado de dicha acción (que será una vista parcial) en el div cuyo id es “datadiv”.



Este es el código HTML generado por esta vista en MVC2:




<h2>Normal Ajax</h2>
<form action="/Home/PostData" method="post"
onclick="Sys.Mvc.AsyncForm.handleClick(this, new Sys.UI.DomEvent(event));"
onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, httpMethod: &#39;Post&#39;, updateTargetId: &#39;datadiv&#39; });">
<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
</form>
<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>





Fijaos en que en el tag <form> se le ha incrustado código javascript para gestionar el onclick y el onsubmit (para poder realizar el envío via ajax).



Bien… y esta misma vista (idéntica) que código genera en MVC3? Pues el siguiente:




<h2>Normal Ajax</h2>
<form action="/Home/PostData"
data-ajax="true" data-ajax-method="Post" data-ajax-mode="replace"
data-ajax-update="#datadiv" method="post">

<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
</form>

<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>





Fijaos que diferencia… No hay nada de javascript mezclado en el código. Todo es HTML. Simplemente al tag <form> se le añaden unos cuantos atributos (los que empiezan por data-ajax) que indican como se debe comportarse este formulario a nivel de Ajax.



Y quien realiza “la magia”? Pues quien va a ser… nuestra amada jQuery, junto con una extensión de Microsoft (el fichero jquery.unobtrusive-ajax.js)! Para que esto funciona teneis que añadir tanto jQuery como la extensión de MS (yo los pongo en la master):




<script src="<%: Url.Content("~/Scripts/jquery-1.4.1.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.unobtrusive-ajax.js") %>" type="text/javascript"></script>





Esta adaptación de los helpers en MVC3 para soportar esta característica es a lo que nos referimos cuando decimos que “ASP.NET MVC3 da soporte para Unobtrusive Ajax”, y es una doble gran noticia. Digo doble porque por un lado nos permite seguir usando los helpers con la garantía de que vamos a generar código “limpio” de javascript y por otro lado el helper de Ajax usa ¡por fin! jQuery. A diferencia de MVC2 donde el Helper Ajax usaba la Ajax Library de Microsoft. De hecho, aunque en los templates de proyecto se sigue poniendo, si me aceptas un consejo: bórrala y no la uses. Puedes borrarla con total tranquilidad porque en MVC3 ningún helper la usa.



Unobtrusive Ajax viene habilitado por defecto en los nuevos proyectos MVC3 pero lo podéis deshabilitar (y entonces generar el mismo código que en MVC2, usando la Microsoft Ajax Library). Podeis deshabilitarlo a nivel de vista o para todo el proyecto.



Para deshabilitarlo a nivel de vista, basta con incluir:




<% HtmlHelper.UnobtrusiveJavaScriptEnabled = false; %>





Para deshabilitarlo para todo el proyecto, puedes incluir ese mismo código en el global.asax.cs o bien usar web.config:




<configuration>
<appSettings>
<add key="UnobtrusiveJavaScriptEnabled" value="false"/>
</appSettings>
</configuration>





Lo mismo para habilitarlo. Si no aparece la entrada UnobtrusiveJavaScriptEnabled en el <appSettings> el valor por defecto es false. Es por eso que si haces un upgrade de un proyecto de MVC2 a MVC3, no tendrás esta entrada en el web.config y por eso Unobtrusive Ajax estará deshabilitado!



Un saludo!



PD: El hecho de que los atributos que se usan para que Unobtrusive Ajax funcione empiecen por “data-“ es porque HTML5 reserva estos atributos “para usos propios de los scripts del site”, tal y como podéis leer en la especificación de Custom Data Attributes.



PD2: Para variar… otro crosspost desde mi blog en geeks.ms! ;-)