Cambiar el esquema con ASP.NET Identity

Una de las grandes novedades (probablemente la de mayor calado si obviamos la revolución de OWIN) de la nueva versión de ASP.NET es ASP.NET Identity, que sustituye al viejuno Membership (que apareció allá en 2005). ASP.NET Identity está diseñado para dar solución a muchos de los problemas de los que Membsership acaecía.

Una de las preguntas más recurrentes en los foros de la MSDN y fuera de ellos es como usar Membership con un esquema de base de datos propio. Esto básicamente implica crearte un custom memberhip provider lo que, sin ser excesivamente complicado, te hace bueno… derivar de una clase con un porrón de métodos abstractos, la mitad de los cuales puede que ni apliquen en tu caso y que vas a dejar llenos de NotImplementedException. Pesado, feo y además un NotImplementedException siempre demuestra un fallo de diseño en algún punto del sistema (en este caso en el Membership).

ASP.NET Identity ha sido diseñado, entre otras cosas, para solucionar este problema: que los cambios de esquema de la BBDD no impliquen tantos problemas. En este post veremos como adaptar ASP.NET Identity a un esquema de BBDD propio, utilizando, eso sí, el proveedor de ASP.NET Identity para Entity Framework. Y es que otras de las características de ASP.NET Identity es que puede trabajar con varios proveedores de datos (p. ej. se podría hacer un proveedor noSQL para MongoDb) aunque, obviamente (no le pidamos peras al olmo) implementar un proveedor nuevo no es algo trivial. MS ha implementado uno para EF que es el que mucha gente va a utilizar, así que centrémonos en él. Dicho proveedor reside en el paquete Microsoft.AspNet.Identity.EntityFramework y se incorpora por defecto cuando seleccionamos la opción “Individual User Accounts” al crear un proyecto ASP.NET en VS2013.

Si no hacemos nada, el esquema por defecto que se nos crea es el siguiente:

image

Miremos ahora alguna de las tablas, p. ej. AspNetUsers:

image

Bien, veamos ahora como podemos añadir un campo adicional a esta tabla.

Añadiendo un campo adicional a la tabla de usuarios

Honestamente este es la parte fácil (y la que encontrarás en la mayoría de tutoriales sobre ASP.NET Identity). Pero antes de ver como hacerlo déjame contarte un par de cosillas sobre como está montado el proveedor de ASP.NET Identity para EF. Dicho proveedor se basa en EF Code First y en varias clases que implementan las interfaces necesarias para ASP.NET Identity. P. ej. ASP.NET Identity trabaja con la interfaz IUser y el módulo de EF utiliza la clase IdentityUser. La interfaz IUser es muy simple, solo define dos campos (Id y UserName ambos de tipo string). La clase IdentityUser implementa dicha interfaz y añade varios campos más (como PasswordHash y SecurityStamp). Podríamos decir que la tabla AspNetUsers se genera a partir de dicha clase.

Nosotros no podemos modificar dicha clase y añadir campos, así que el proveedor de ASP.NET Identity para EF ha optado por ofrecernos otro mecanismo. Dicho proveedor para trabajar necesita un contexto de EF (es decir un DbContext), pero dicho DbContext debe derivar de IdentityDbContext<T> donde el tipo T derive de IdentityUser. Así pues para añadir un campo simplemente debemos:

  1. Crear una clase X derivada de IdentityUser y añadir el campo necesario.
  2. Derivar de IdentityDbContext<X> donde X es la clase creada en el punto anterior.

El template de VS2013 nos crea una clase llamada ApplicationUser que ya hereda de IdentityUser. Dicha clase está vacía y está pensada para que nosotros añadamos los campos necesarios. También nos crea un contexto de EF (llamado ApplicationDbContext) que hereda de IdentityDbContext<ApplicationUser>. Es decir, nos da casi todo el trabajo hecho 🙂

Para no ser originales, vamos a hacer el ejemplo clásico que encontrarás en cualquier post: añadir la fecha de nacimiento. Para ello nos basta con añadir dicho campo en la clase ApplicationUser:

  1. public class ApplicationUser : IdentityUser
  2. {
  3.     public DateTime? BirthDay { get; set; }
  4. }

Si ahora ejecutas de nuevo la aplicación, seguramente te dará el siguiente error: The model backing the ‘ApplicationDbContext’ context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)

Esto es porque se ha modificado el esquema de datos y ahora tenemos un código que no se corresponde a la BBDD. Tenemos dos opciones: o eliminar la BBDD (y perder todos los datos) o usar Migrations. Para usar Migrations tenemos que habilitarlas primero mediante el Package Manager Console, tecleando Enable-Migrations. Una vez las tengas habilitadas debemos:

  1. Añadir una migración que refleje el cambio realizado en ApplicationUser, usando Add-Migration <nombre_migracion> en el Package Manager Console
  2. Actualizar la BBDD usando Update-Database en el Package Manager Console.

En mi caso cuando he tecleado Add-Migration AddBirthday me ha generado dentro de una carpeta Migrations el archivo de migración. Dicho archivo es una clase C# que le indica a Migrations que hacer. Se genera automáticamente “comparando” el esquema de la BBDD con el esquema que tendría que haber según la clases de EF code first. Para que veas, en mi caso la clase de migración contiene el siguiente código:

  1. public partial class AddBirthday : DbMigration
  2. {
  3.     public override void Up()
  4.     {
  5.         AddColumn("dbo.AspNetUsers", "BirthDay", c => c.DateTime());
  6.     }
  7.         
  8.     public override void Down()
  9.     {
  10.         DropColumn("dbo.AspNetUsers", "BirthDay");
  11.     }
  12. }

Puedes ver que lo que hará es añadir la columna BirthDay. Insisto: dicha clase se genera automáticamente al hacer Add-Migration. Cuando ejecutes Update-Database, se aplicarán los cambios indicados.

Así que bueno… lo único que te queda es esto, ejecutar el comando Update-Database desde el Package Manager Console. Cuando lo apliques verás algo parecido a esto:

image

Listos… Si ahora miras el esquema de la tabla AspNetUsers verás que tenemos ya el nuevo campo:

image

Fácil y sencillo. La verdad, comparado con lo que se tenía que hacer para conseguir lo mismo con el Membership es una maravilla 😛

Cambiar el esquema

Vale… añadir campos es sencillo (hemos visto el ejemplo clásico que es la tabla de usuarios, para el resto de tablas es lo mismo pero con otras clases). Pero… y cambiar el esquema? Si quiero que la tabla AspNetUsers se llame simplemente Users y que PasswordHash se llame Pwd p. ej.? Que tenemos que hacer?

Pues bien, eso también es posible gracias al uso de EF Code First. En este caso vamos a tener que tocar el contexto de EF (la clase ApplicationDbContext en el caso de código generado por VS2013), en concreto redefinir el método OnModelCreating:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3.     base.OnModelCreating(modelBuilder);
  4.     modelBuilder.Entity<IdentityUser>().ToTable("Users").
  5.         Property(u => u.PasswordHash).HasColumnName("Pwd");
  6.     modelBuilder.Entity<ApplicationUser>().ToTable("Users").
  7.         Property(u => u.PasswordHash).HasColumnName("Pwd");
  8. }

Y ahora el esquema que tengo en la BBDD es (mantengo mi clase ApplicationUser con la propiedad BirthDay):

image

De nuevo… comparado con lo que se tenía que hacer con el antiguo Membership es una maravilla.

El tipo de cambios que puedes hacer vienen limitados por EF Code First, pero en general estamos ante un modelo mucho más flexible que el anterior Membership.

En resumen, a pesar de que ASP.NET Identity tiene varias cosillas que no me gustan (o mejor dicho, realmente varias cosas que echo en falta) es sin duda un paso adelante respecto al anterior Membership. Me sigue quedando la duda de si lo que ofrece es suficiente para según que tipo de webs, pero si las funcionalidades que ofrece te son suficientes y no te importa ajustar tu esquema a algunos detalles menores (como esos Id de usuario de tipo string) es un avance muy significativo respecto a Membership.

Un saludo a todos y… ¡felices fiestas!

Integrando oAuth con NancyFx (ii) – Katana

En el post anterior vimos como autenticar una aplicación NancyFx usando oAuth a través del paquete WorldDomination (o SimpleAuthentication que es el aburrido nombre que tiene ahora :p).

Pero dado que NancyFx puede funcionar como un componente OWIN y la estructura modular de OWIN permite que haya módulos de autenticación que se ejecuten antes en el pipeline, porque no “eliminar” toda responsabilidad sobre autenticación de NancyFx? Y que sea algún módulo OWIN el que lo haga no? A fin de cuentas, esa es la gracia de OWIN. En este post vamos a ver como integrar los módulos OWIN de autenticación que tiene Katana con NancyFx. Repasa el post anterior y haz lo siguiente:

  1. Crea una aplicación web vacía y añade los paquetes de Nancy, Nancy.Owin y Microsoft.Host.SystemWeb.
  2. Crea la clase de inicialización OWIN para que use Nancy.
  3. Crea un  módulo que redirija las peticiones a la URL / a una vista que muestre un enlace de login with twitter (que vaya p. ej. a /login/twitter).
  4. Crea otro módulo que redirija las peticiones de /secured a otra vista (basta que muestre un contenido tipo “Esto es seguro”.

Quédate aquí. En este punto puedes tanto acceder a / como a /secured obviamente, y pulsar en enlace “login with twitter” te generará un 404.

Pero ahora estamos listos para empezar.

Nota: Para este post vamos a usar la misma aplicación en twitter (que tenía el callback a /authentication/authenticatecallback. Aunque ahora la URL de callback puede ser la que queramos.

Dejando que Katana hable con Twitter…

Katana incorpora varios componentes de autenticación y hay uno que se encarga precisamente de gestionar el flujo oAuth con twitter. Este componente se llama Microsoft.Owin.Security.Twitter así que añádelo al proyecto. Para entendernos es el equivalente al WorldDomination pero en un mundo OWIN.

Una vez hayas añadido este paquete el primer paso es modificar la clase de inicio de OWIN para añadir el módulo de autenticación por Twitter:

  1. app.UseTwitterAuthentication(new TwitterAuthenticationOptions()
  2. {
  3.     ConsumerKey = "TU COMSUMER KEY",
  4.     ConsumerSecret = "TU CONSUMER SECRET"
  5. });

Por supuesto pon el consumer key y consumer secret de tu aplicacion.

En este punto si pulsas el enlace de “login with twitter” recibirás… un 404 de Nancy. Pues este enlace apunta a /login/twitter (o a la URL que tu hayas elegido, en el fondo da igual) y es una URL que no está enrutada. A diferencia del post anterior donde WorldDomination ya gestionaba la URL “/authentication/redirect/twitter” el módulo de Katana no gestiona ninguna URL. En su lugar “entra en acción” tan buen punto se recibe un 401.

Así que nada, vamos a añadir un  módulo que enrute la URL /login/twitter y devuelva un 401:

  1. public class AuthTwitterModule : NancyModule
  2. {
  3.     public AuthTwitterModule()
  4.     {
  5.         Get["/login/twitter"] = _ => new Response()
  6.         {
  7.             StatusCode = HttpStatusCode.Unauthorized
  8.         };
  9.     }
  10. }

Ahora si navegas a /login/twitter lo que recibirás es… bueno un 401 😛 La razón es porque aunque el módulo de Katana entra en acción cuando recibe un 401, no basta solo con el 401. Antes requiere que se rellene el entorno de OWIN con cierta información.

El entorno de OWIN es un diccionario de objetos, literalmente un IDictionary<string, object> que contiene toda la información del pipeline de OWIN. En OWIN no hay objetos tipo Request, Response o HttpContext porque eso implicaría que existe alguna DLL principal de OWIN y OWIN no pretende eso: se basa en tipos de .NET (Hay una sola excepción a este caso y es la interfaz IAppBuilder que está definida en el paquete Owin). Así los módulos OWIN se pasan información entre ellos a través de ese diccionario compartido.

Por lo tanto antes de devolver el 401 debemos meter cierta información en el entorno de OWIN para que el módulo de autenticación de Katana sepa que queremos autenticarnos via Twitter. ¿Qué método hay en NancyFx para meter código antes del código que procesa la petición? Exacto, el module hook Before. Pero en este caso no añadiremos el código directamente en el Before (podríamos) pero lo haremos más reutilizable a través de métodos de extensión (así seria aplicable a más de un módulo).

Pero primero necesitamos un método de extensión que me permita obtener el entorno de OWIN. El paquete Nancy.Owin (que es quien gestiona la integración de NancyFx en OWIN) deja el entorno OWIN dentro de la clave NancyOwinHost.RequestEnvironmentKey del contexto de NancyFx:

  1. public static class NancyContextExtensions
  2. {
  3.     public static OwinContext GetOwinContext(this NancyContext context)
  4.     {
  5.         var environment = (IDictionary<string, object>)context.Items[NancyOwinHost.RequestEnvironmentKey];
  6.         var owinContext = new OwinContext(environment);
  7.         return owinContext;
  8.     }
  9. }

A partir de la información del entorno se crea una variable de tipo OwinContext. La clase OwinContext no es estandard OWIN. Esta clase es una clase de Katana. Así que para que no queden dudas: la integración que estamos haciendo es entre NancyFx y los componentes de Katana. De hecho no es posible una integración universal porque la especificación de OWIN no define el nombre de las claves del entorno que los módulos deben usar, salvo unas cuantas (que podéis encontrar en la especificación de OWIN). Así pues la clase OwinContext no es nada más que el entorno de OWIN, pero visto a través de algo más tipado que un IDictionary<string, object> y que además entiende las claves que usan los módulos de Katana.

Vale, ahora que ya tenemos como obtener el entorno OWIN, vamos a añadir otro método de extensión, pero ahora contra la clase NancyModule:

  1. static class NancyModuleExtensions
  2. {
  3.     public static void Challenge(this NancyModule module, string redirectUri, string userId)
  4.     {
  5.         module.AddBeforeHookOrExecute(ctx =>
  6.         {
  7.             var properties = new AuthenticationProperties() { RedirectUri = redirectUri };
  8.             if (userId != null)
  9.             {
  10.                 properties.Dictionary["XsrfId"] = userId;
  11.             }
  12.             module.Context.GetOwinContext().Authentication.Challenge(properties, "Twitter");
  13.             return null;
  14.         }, "Challenge");
  15.     }
  16. }

Lo que estamos haciendo es añadir código al module hook Before del módulo de NancyFx al que se llame este método (sería lo más parecido a crear un filtro en ASP.NET MVC que hay en Nancy). Básicamente lo que hacemos en el Before es llamar al método Challenge que proporciona Katana que es el que se encarga de todo lo necesario.

Ahora tenemos que modificar el AuthTwitterModule para añadir la llamada a este método de extensión:

  1. public AuthTwitterModule()
  2. {
  3.     this.Challenge("/auth/redirect", null);
  4.     Get["/login/twitter"] = _ => new Response()
  5.     {
  6.         StatusCode = HttpStatusCode.Unauthorized
  7.     };
  8. }

Ahora sí, si navegas a /login/twitter empezará el flujo de oAuth y después serás redirigido a la URL que hemos usado como primer parámetro de la llamada a Challenge, es decir /auth/redirect con independencia del valor de callback que teníamos especificado en la aplicación de twitter.

En esta URL de callback (/auth/redirect) tenemos que recoger los valores que nos haya devuelto el proveedor de oAuth. Para ello nos vamos a apoyar en otro componente de Katana, el paquete Microsoft.AspNet.Identity.Owin, así que añade este paquete ahora. Una vez lo hayas añadido podemos usar el método GetExternalLoginInfoAsync.

Este método es asíncrono, así que lo invocaremos con await. Para poder usar await en NancyFx tenemos que declarar que la ruta que responde a /auth/redirect es asíncrona:

  1. Get["/auth/redirect", true] = async (_, ct) =>
  2. {
  3.     var authManager = Context.GetOwinContext().Authentication;
  4.     var loginInfo = await authManager.GetExternalLoginInfoAsync();
  5.     // …
  6. };

Este método se encarga de obtener los datos que nos envía el proveedor de oAuth. En este punto podemos recoger los datos del usuario y crear un ClaimsIdentity (esa clase es la nueva clase base de todas las identity en .NET). Lo más normal sería delegar en el Identity Membership para esto, pero, para no saturar, hagámoslo a mano. En el siguiente post veremos como integrarnos con  el Identity Membership. El código podría ser algo como así:

  1. Get["/auth/redirect", true] = async (_, ct) =>
  2. {
  3.     var authManager = Context.GetOwinContext().Authentication;
  4.     var loginInfo = await authManager.GetExternalLoginInfoAsync();
  5.     if (loginInfo != null)
  6.     {
  7.         authManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
  8.  
  9.         var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie.ToString(),
  10.             "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
  11.             "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
  12.         identity.AddClaim(new Claim("Player", "True"));
  13.         identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
  14.             loginInfo.DefaultUserName,
  15.             "http://www.w3.org/2001/XMLSchema#string"));
  16.         authManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, identity);
  17.     }
  18.  
  19.     return new RedirectResponse("/secured");
  20. };

No te preocupes si no entiendes exactamente el código (como digo eso suele delegarse en el Identity Membership), pero básicamente creamos el ClaimsIdentity y le añadimos un nombre de usuario, así como una claim personalizada (Player con valor True).

Al final redirigimos al usuario a la URL /secured, una URL que se supone solo debe poder verse si el usuario no está autenticado.

El código del módulo Nancy que contiene la ruta para dicha URL es el siguiente:

  1. public class SecuredModule : NancyModule
  2. {
  3.     public SecuredModule()
  4.     {
  5.         this.RequiresOwinAuth();
  6.         Get["/secured"] = _ => View["secured.html"];
  7.     }
  8. }

El método RequiresOwinAuth es un método de extensión que nos hemos creado. Dicho método comprueba que existe una ClaimsIdentity en el contexto OWIN:

  1. public static void RequiresOwinAuth(this NancyModule module)
  2. {
  3.     module.AddBeforeHookOrExecute(ctx =>
  4.     {
  5.         var user = ctx.GetOwinUser();
  6.         return user == null || !user.Identity.IsAuthenticated ?
  7.             new Response() {StatusCode = HttpStatusCode.Unauthorized} :
  8.             null;
  9.     }, "OwinUser Not Found");
  10. }

(Este método de extensión sería el equivalente a aplicar [Authorize] en un controlador ASP.NET MVC).

Vale… ya casi lo tenemos, ahora tan solo nos falta configurar el pipeline OWIN para añadir la seguridad por cookies:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions()
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
  4. });

Añadimos este código al principio del método de inicialización de OWIN, para que este módulo esté en el principio del pipeline.

Y ¡voilá! hemos terminado. Si nada más iniciar la aplicación navegas a /secured recibirás un 401 (Unauthorized). Si entras en twitter, después de hacer el login verás como se te redirige a /secured y ahora si ves el contenido seguro.

Por supuesto, puedes hacer que en lugar de ver un 401 el usuario sea redirigido a una página de Login, simplemente cambiando la configuración del proveedor de seguridad por cookies:

  1. app.UseCookieAuthentication(new CookieAuthenticationOptions()
  2. {
  3.     AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
  4.     LoginPath = new PathString("/login")
  5. });

Ahora si nada más empezar navegas a /secured verás que el usuario es redirigido a /login (en este caso verás un 404 ya que no hay ninguna ruta que responda a la URL /login).

Bueno… en el post anterior vimos como configurar NancyFx junto con WorldDomination para soportar login por oAuth. En este post hemos ido un paso más allá sustituyendo toda la autenticación por componentes OWIN, en lugar de que toda la responsabilidad esté gestionada por NancyFx.

Un saludo!

PD: Tenéis el código en mi carpeta de SkyDrive (fichero NancyKatanaIIS2).

Integrando oAuth con NancyFx–(i)

Buenas! El objetivo de este post es explicar la solución a la que he llegado para integrar autenticación con oAuth en un sitio web desarrollado con NancyFx. En este primer post veremos como hacerlo “de la forma clásica” pero en otro siguiente nos aprovecharemos de los componentes de autenticación de Katana.

Iré hablando en este blog más sobre OWIN, Katana y también sobre NancyFx, pero por el momento algunas definiciones rápidas:

  • OWIN: Especificación que indica como deben comunicarse los servidores web, el middleware web y las aplicaciones web en .NET.
  • Katana: Implementación de varios componentes (hosts, servidores, middlewares varios) OWIN por parte de Microsoft.
  • NancyFx: Un framework basado en el patrón MVC para desarrollar aplicaciones web y APIs REST. Por decirlo de algún modo NancyFx “compite” con ASP.NET MVC y con WebApi a la vez.

Para saber más de OWIN hay un par de posts en mi blog pero también una serie fenomenal del Maestro. Échales un ojo.

Creando una aplicación Nancy ejecutándose en IIS

Vamos a crear una aplicación web, y vamos a usar NancyFx en lugar de ASP.NET, pero usando IIS (IISExpress).

NancyFx viene en dos “sabores”: puede ser un HttpHandler de ASP.NET (así instalas y configuras NancyFx usando el web.config), pero también existe como componente OWIN, lo que permite su uso dentro de cualquier entorno OWIN.

Para usar NancyFx como HttpHandler de ASP.NET debes instalar el paquete Nancy.Hosting.Aspnet que es el que contiene todas las referencias a ASP.NET (NancyFx es totalmente independiente de ASP.NET). Ahora vamos a crear una aplicación web ASP.NET, así que lo lógico parece ser usar NancyFx como HttpHandler y listos. En muchos casos puede ser lo más lógico, pero no es lo que vamos a hacer. No vamos a usar NancyFx como HttpHandler de ASP.NET. En su lugar lo vamos a utilizar como componente OWIN y, para ello, nos aprovecharemos de un componente de Katana que permite usar componentes OWIN dentro del pipeline de ASP.NET (en terminología OWIN diríamos que este componente de Katana implementa un Host y un servidor Web OWIN). Esto parece dar muchas vueltas, y en muchos casos así puede ser, pero tiene las siguientes ventajas:

  1. Dado que nuestra aplicación será OWIN en cualquier momento podremos abandonar el cómodo regazo de IIS e irnos a cualquier otro host o servidor OWIN.
  2. Incluso aunque no tengamos pensado divorciarnos de IIS, podremos utilizar cualquier componente OWIN que haya por ahí 😉

Como digo, para poder utilizar NancyFx como componente OWIN pero ejecutándose en un pipeline de ASP.NET necesitamos la ayuda de Katana, en concreto del componente Microsoft.Owin.Host.SystemWeb.

Así, los pasos para usar NancyFx como componente OWIN dentro del pipeline de ASP.NET son los siguientes. Antes que nada crea un proyecto de tipo ASP.NET y selecciona el template “Empty”:

image

Luego instala los siguientes paquetes de NuGet:

  • Nancy (el core de NancyFx)
  • Nancy.Owin (Para usar Nancy como módulo OWIN)
  • Microsoft.Owin.Host.SystemWeb (Para usar módulos OWIN en el pipeline de ASP.NET)

Con esto ya tenemos el esqueleto necesario.

El siguiente paso es crear la clase de inicialización de OWIN. Para ello agrega una clase normal y llámala Startup:

Clase de inicializacion OWIN
  1.     public class Startup
  2.     {
  3.         public void Configuration(IAppBuilder app)
  4.         {
  5.             app.UseNancy();
  6.         }
  7.     }

Es importante que la clase se llame Startup. Esto es una convención que sigue Katana (pues quien inicializa los módulos OWIN es Katana a través del paquete Microsoft.Owin.Host.SystemWeb). Si nuestra clase tiene otro nombre recibirás un error como el siguiente:

image

Una solución es o bien renombrar la clase para que se llame Startup o en el caso de que no quieras hacerlo usar el atributo OwinStartupAttribute:

  1. [assembly: OwinStartup(typeof(MyCustomStartup))]

Si ahora ejecutas la aplicación deberías ver algo parecido a lo siguiente:

image

Eso significa que Nancy está funcionando correctamente (este 404 ha sido servido por Nancy). ¡Felicidades! Ya tienes a Nancy corriendo bajo IIS.

Vamos ahora a crear un módulo de Nancy (más o menos equivalente a un controlador de ASP.NET MVC), para gestionar la llamada a la URL / y mostrar una vista con solo el enlace “Login with twitter”. Añade una clase a tu proyecto con el siguiente código:

Nancy MainModule
  1. public class MainModule : NancyModule
  2. {
  3.     public MainModule()
  4.     {
  5.         Get["/"] = _ => View["main.html"];
  6.     }
  7. }

Con esto has creado un módulo de NancyFx y has enrutado todas las peticiones que vayan a la URL “/” para que muestren la vista main.html. Añade pues un archivo main.html (no es necesario que esté en ninguna carpeta Views ni nada) que muestre un enlace a la URL “/authentication/redirect/twitter”.

Si ahora ejecutas el proyecto deberías se tendría que ver el contenido del fichero main.html. Y si pulsas sobre el enlace tienes que recibir el 404 de Nancy.

Perfecto! Ya tenemos el esqueleto base de la aplicación. Ahora vayamos a integrar la autenticación por twitter.

Integrando oAuth con NancyFx a la manera clásica

Nota: El contenido de este apartado presupone que tienes creada una aplicación en twitter y que por lo tanto tienes un consumer key y un consumer secret. También debes configurar la aplicación twitter para que la URL de callback sea /authentication/authenticatecallback">http://localhost:<puerto>/authentication/authenticatecallback

Nota 2: Twitter (y otros proveedores oAuth) no dejan dar de alta aplicaciones cuyo callback sea una dirección de localhost. En este caso lo más rápido es usar el dominio xxx.localtest.me (usa cualquier valor para xxx). El dominio localtest.me está especialmente pensado para estos casos: cualquier subdominio en localtest.me resuelve a 127.0.0.1 😉

NancyFx es un framework muy modular, y no tiene incorporado el concepto de autenticación o autorización. Eso significa que no hay por defecto ninguna API ni nada que nos diga si el usuario está autenticado o bien poder autenticarlo. Todo eso se deja a implementaciones “externas” al core de NancyFx.

P. ej. si queremos autenticar nuestra aplicación basándonos en cookies (lo que en el mundo ASP.NET conocemos como autenticación por forms) debemos instalar el paquete Nancy.Authentication.Forms. Hay otros paquetes para otros tipos de autenticación (como puede ser Nancy.Authentication.Basic para autenticación básica de HTTP o bien Nancy.Authentication.Stateless para basar la autenticación en alguna cabecera específica de la petición). Todos estos paquetes (y más que hay para otros tipos de autenticación) se basan en el modelo de extensibilidad de NancyFx.

Por supuesto hay un paquete para integrarnos con oAuth que antes respondía al interesante nombre de Nancy.Authentication.WorldDomination pero que ahora tiene el aburrido y anodino nombre de Nancy.SimpleAuthentication. Así que añade este paquete a tu proyecto y ya estarás listo para iniciar un flujo para autenticación.

Al añadir este paquete tu web.config se habrá modificado y se habrán añadido las siguientes líneas:

Code Snippet
  1.   <configSections>
  2.     <section name="authenticationProviders" type="SimpleAuthentication.Core.Config.ProviderConfiguration, SimpleAuthentication.Core" />
  3.   </configSections>
  4.   <authenticationProviders>
  5.     <providers>
  6.       <add name="Facebook" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  7.       <add name="Google" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  8.       <add name="Twitter" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  9.       <add name="WindowsLive" key="please-enter-your-real-value" secret="please-enter-your-real-value" />
  10.     </providers>
  11.   </authenticationProviders>

En nuestro caso podemos dejar solo la información del provider de Twitter y debeis rellenar el valor de consumer key y el consumer sectret que os provee Twitter. Acuérdate tambien de mover el tag <configSections> para que sea el primero dentro del <configuration> en el web.config, si no IIS os dará un error!

Ahora el siguiente paso es crear una clase que implemente IAuthenticationCallbackProvider:

Code Snippet
  1. public class MyCustomAuthCallbackProvider : IAuthenticationCallbackProvider
  2. {
  3.     public dynamic Process(NancyModule nancyModule, AuthenticateCallbackData model)
  4.     {
  5.         // En caso de autenticacin OK
  6.     }
  7.  
  8.     public dynamic OnRedirectToAuthenticationProviderError(NancyModule nancyModule, string errorMessage)
  9.     {
  10.         // En caso de error
  11.     }
  12. }

Ejecuta de nuevo tu aplicación. Ahora si pulsas sobre el enlace de login with twitter… ¡deberías ver la página de Twitter!:

image

Una vez el usuario haya dado sus datos y haya autorizado a tu aplicación entonces se ejecuta el método Process de la clase que acabamos de crear. En el parámetro “model” tendrás toda la información necesaria:

image

Una vez sabes cual es el usuario autenticado debes autenticarlo en tu propia web. Es decir Twitter nos ha dado el OK, pero ahora lo que nos falta es utilizar algún mecanismo para autenticar todas las peticiones que realice este usuario en nuestra web.

Nota: Para que todo funcione recuerda que la URL de callback de la aplicación en twitter debe apuntar a /authentication/authenticatecallback y que el enlace de “Login with twitter” debe apuntar a /authentication/redirect/twitter. Esas dos URLs son gestionadas automáticamente por el paquete WorldDomination.

Si queremos utilizar una cookie, tenemos que instalar el paquete Nancy.Authentication.Forms, así… que nada, a por él!

Añadiendo la seguridad por Forms

Una vez tenemos el paquete Nancy.Authentication.Forms añadido ya podemos configurarlo. Son necesarios 3 pasos para que todo funcione correctamente.

El primero es crear una clase que implemente la interfaz IUserIdentity. Esta interfaz es toda la información que el core de Nancy mantiene sobre un usuario autenticado (nombre y permisos):

  1. public class AuthenticatedUser : IUserIdentity
  2. {
  3.     public string UserName { get; set; }
  4.     public IEnumerable<string> Claims { get; set; }
  5. }

El segundo es crear un “User Mapper”. Esto vendría a ser el equivalente (solo lectura) del Membership Provider en ASP.NET. Es decir el encargado de obtener los datos del usuario del repositorio donde se guarden:

  1. public class MyUserMapper : IUserMapper
  2. {
  3.  
  4.     public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
  5.     {
  6.         // Obtendriamos el usuario de la BBDD
  7.         return new AuthenticatedUser()
  8.         {
  9.             UserName = "eiximenis",
  10.             Claims = new[] {"Read", "Write", "Admin"}
  11.         };
  12.     }
  13. }

En Nancy todos los usuarios se identifican por un Guid (por supuesto esto no significa que en la BBDD este Guid tenga que ser la PK de la tabla de usuarios!).

El tercer y último paso es indicar al core de Nancy que queremos autenticación por formularios:

  1. public class Bootstrapper : DefaultNancyBootstrapper
  2. {
  3.     protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
  4.     {
  5.         base.RequestStartup(container, pipelines, context);
  6.         var formsAuthConfiguration = new FormsAuthenticationConfiguration
  7.         {
  8.             DisableRedirect = true,
  9.             UserMapper = new MyUserMapper()
  10.         };
  11.         FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
  12.     }
  13. }

El Bootstrapper es la clase que inicializa todo el core de Nancy. Hasta ahora no teníamos, así que lo añadimos y listos (no hay que indicar en ningún sitio cual es nuestro Bootstrapper, se descubre automáticamente). Con esto ya tenemos la autenticación por forms habilitada en nuestro proyecto.

Volvamos ahora a nuestro CallbackProvider. Lo que haríamos en el método Process es:

  • Consultar la BBDD de usuarios para encontrar el usuario que se corresponda con los datos que nos ha devuelto twitter.
  • En caso de no existir crearlo y obtener su Guid.
  • En caso de existir obtener su Guid.
  • Una vez tenemos el Guid llamar al método LoginAndRedirect. Este método vendría a ser el equivalente al SetAuthCookie de ASP.NET:
  1. public dynamic Process(NancyModule nancyModule, AuthenticateCallbackData model)
  2. {
  3.     // Accederiamos a BBDD para obtener el GUID del usuario
  4.     // O si no lo crearamos
  5.     var userGuid = Guid.NewGuid();
  6.     return nancyModule.LoginAndRedirect(userGuid);
  7. }

¡Y listos! ¡Has terminado!

Añadir contenido securizado

Vamos a añadir un módulo a Nancy que solo se ejecute si el usuario está autenticado. En ASP.NET MVC meterías un [Authorize] en la acción del controlador. En Nancy lo equivalente es usar el module hook Before. Cada modulo de Nancy tiene una propiedad Before en la que puedes poner código que se ejecuta antes de que se ejecute cualquier otro código del módulo (es decir, el código correspondiente a la petición). Desde este código puedes verificar si el usuario está autenticado:

  1. public class SecureModule : NancyModule
  2. {
  3.     public SecureModule()
  4.     {
  5.         Before += ctx =>
  6.         {
  7.             if (ctx.CurrentUser == null)
  8.                 return new Response()
  9.                 {
  10.                     StatusCode = HttpStatusCode.Unauthorized
  11.                 };
  12.             return null;
  13.         };
  14.  
  15.         Get["/Secure"] = _ => View["secure.html",
  16.             new
  17.             {
  18.                 Name = Context.CurrentUser.UserName
  19.             }];
  20.     }
  21. }

Si la propiedad del CurrentUser del contexto es igual a null devolvemos un 401. En caso contrario no hacemos nada y dejamos que siga el pipeline de Nancy (por lo que se ejecutará el código que toque según la URL).

Para terminar añade el archivo secure.html:

  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title></title>
  5. </head>
  6. <body>
  7.     Hello @Model.Name
  8. </body>
  9. </html>

(Aunque lo parezca esto NO es Razor. Es el view engine por defecto de Nancy que se llama SSVE (Super Simple View Engine)).

Ya estamos listos para probar! Ejecuta el proyecto y navega a /secure. Deberías ver una página sin nada, pero un vistazo a la pestaña Network de las developer tools nos informa que estamos teniendo un 401:

image

Pefecto. Vuelve a la raíz, y autentícate por twitter. Después del proceso de autenticación volverás a la raiz. Navega a /secure de nuevo y ahora deberías ver la vista secure.html:

image

(Recuerda que todos los usuarios se llamarán eiximenis ya que nuestro IUserMapper siempre devuelve lo mismo ;))

Y hemos finalizado! Ya tienes tu web con Nancy autenticada mediante oAuth. Hemos elegido twitter pero para el resto de proveedores no hay muchas diferencias (el mérito es todo de WorldDomination).

Unas palabras finales

Hemos utilizado Katana para usar NancyFx en “modo OWIN” bajo el pipeline de ASP.NET en IIS. Esto nos permitiría integrarnos con otro middleware OWIN. Así pues, y esta es precisamente la idea de OWIN, podría usar un middleware OWIN para autenticar y autorizar las peticiones. Es decir, no delegar la autenticación y la autorización en el propio NancyFx si no tener un módulo OWIN encargado precisamente de esto…

… en el siguiente post veremos como 😉

Saludos!

PD: Tienes el proyecto completo (VS2013) en http://sdrv.ms/18brcO7 (Archivo NancyKatanaIIS.zip)

El fallo de ASP.NET MVC y el helper Html.DropDownFor

Buenas! Este post es para describir un fallo que he encontrado en el helper Html.DropDownFor y el workaround asociado. Quizá alguien entiende que no es un fallo y quizá es capaz de decirme que razón se esconde bajo este comportamiento… Desde mi punto de vista ninguno, pero bueno… ni tengo (ni pretendo tener) la verdad absoluta.

El problema…

Veamos… Para el helper Html.DropDownFor se usa para crear combos y tiene varias formas de uso (yo mismo escribí un post hace algún tiempo al respecto sobre las combos en ASP.NET MVC). En una de sus formas de uso, podemos mostrar una lista de cervezas y guardar la cerveza seleccionada con el siguiente código:

Clase Beer
  1.     public class Beer
  2.     {
  3.         public int Id { get; set; }
  4.         public string Name { get; set; }
  5.     }

En el controlador tenemos una lista de cervezas (_beers) y un par de acciones para mandar una SelectList con esas cervezas.

Acciones del controlador
  1.         public ActionResult Test()
  2.         {
  3.             ViewBag.Beers = new SelectList(_beers, "Id", "Name", 2);
  4.             return View();
  5.         }
  6.  
  7.         [HttpPost]
  8.         public ActionResult Test(BeerSelectViewModel data)
  9.         {
  10.             ViewBag.Beers = new SelectList(_beers, "Id", "Name", data.SelectedBeerId);
  11.             return View();
  12.         }

La vista y el método que gestionan el POST usan un ViewModel para mantener el ID de la cerveza seleccionada:

ViewModel
  1.     public class BeerSelectViewModel
  2.     {
  3.         public int SelectedBeerId { get; set; }
  4.     }

El código de la vista es sencillo:

Vista Test.cshtml
  1. @model WebApplication3.Controllers.BeerSelectViewModel
  2.  
  3. @using (Html.BeginForm())
  4. {
  5.     @Html.DropDownListFor(m => m.SelectedBeerId, ViewBag.Beers as SelectList)
  6.     <p>
  7.         <input type="submit" class="btn-default" value="submit" />
  8.     </p>
  9. }

Fijaos que usamos el constructor de SelectList que acepta el objeto seleccionado (en este caso el ID de la cerveza seleccionada). La primera vez se usa el 2, de forma que Epidor será la cerveza seleccionada por defecto, la primera vez.

Ahora extendamos a que el usuario pueda seleccionar no una, si no DOS cervezas seleccionadas (con dos combos).

Para ello hacemos los siguientes cambios (que al menos a mi me parecen lógicos). Extendemos el ViewModel para que tenga un array de elementos seleccionados:

Nuevo ViewModel
  1. public class BeerSelectViewModel
  2.   {
  3.       public IEnumerable<int> SelectedBeerIds { get; set; }
  4.   }

En el controlador pasamos en el ViewBag la lista de cervezas (en lugar del SelectList), ya que el SelectList lo construiremos en la vista:

Acciones del controlador
  1. public ActionResult Test()
  2.   {
  3.       ViewBag.Beers = _beers;
  4.       var model = new BeerSelectViewModel()
  5.       {
  6.           SelectedBeerIds = new[] {1, 2}
  7.       };
  8.       return View(model);
  9.   }
  10.  
  11.   [HttpPost]
  12.   public ActionResult Test(BeerSelectViewModel data)
  13.   {
  14.       ViewBag.Beers = _beers;
  15.       return View(data);
  16.   }

En la vista iteramos sobre la propiedad SelectedBeersIds y por cada valor construimos una SelectList cuyo cuarto parámetro (elemento seleccionado) sea el ID por el que estamos iterando:

Vista Test.cshtml
  1. @using WebApplication3.Controllers
  2. @model WebApplication3.Controllers.BeerSelectViewModel
  3. @{
  4.     var beers = ViewBag.Beers as IEnumerable<Beer>;
  5. }
  6.  
  7. @using (Html.BeginForm())
  8. {
  9.     foreach (var id in Model.SelectedBeerIds)
  10.     {
  11.         <p>
  12.             @Html.DropDownListFor(m => m.SelectedBeerIds, new SelectList(beers, "Id", "Name", id))
  13.         </p>
  14.     }
  15.     <p>
  16.         <input type="submit" class="btn-default" value="submit" />
  17.     </p>
  18. }

Recordad eso: Estoy indicando a cada Html.DropDownListFor cual es su elemento seleccionado a través del cuarto parámetro de la SelectList que le asocio. Eso debería funcionar… pero no. NO FUNCIONA. En el código HTML generado ningún tag <option> tiene el atributo selected, así que ambas combos muestran el primer elemento… Ahí está el fallo. Le digo a Html.DropDownFor cual debe ser su elemento seleccionado pero el helper hace caso omiso a esta indicación…

… Y la solución

Después de dar vueltas al asunto, llegué a una solución… Primero lo intenté sin usar el helper Html.DropDownFor y usar tan solo Html.DropDown pero el error era el mismo. Al final la solución que encontré fue usar Html.DropDownFor pero contra otra propiedad del ViewModel. Es decir usar una propiedad (SelectedBeerIds para rellenar el elemento seleccionado de las combos y otra propiedad (SelectedBeerIdsNew) para obtener el valor de vuelta (los nuevos elementos seleccionados). Pero ojo, si desde el el método que gestiona el POST debía devolver de nuevo la vista (p. ej. en el caso de que el ModelState no sea válido) entonces debía hacer lo siguiente:

  • Copiar el valor de la propiedad SelectedBeerIdsNew en SelectedBeerIds
  • Eliminar (poner a null) el valor de SelectedBeerIdsNew
  • Eliminar del ModelState la propiedad SelectedBeerIdsNew.

Si no hacemos las dos últimas cosas las combos no respetarán el elemento seleccionado que les pasamos en el SelectList (si no hacemos la primera nos mostrarán los elementos seleccionados anteriores).

El código en el controlador es:

Acciones Controlador
  1. public ActionResult Test()
  2. {
  3.     ViewBag.Beers = _beers;
  4.     var model = new BeerSelectViewModel()
  5.     {
  6.         SelectedBeerIds = new[] {1, 2}
  7.     };
  8.     return View(model);
  9. }
  10.  
  11. [HttpPost]
  12. public ActionResult Test(BeerSelectViewModel data)
  13. {
  14.     ViewBag.Beers = _beers;
  15.     // En este punto en data.SelectedBeerIdsNew tenemos
  16.     // las nuevas cervezas seleccionadas
  17.     data.SelectedBeerIds = new List<int>(data.SelectedBeerIdsNew);
  18.     data.SelectedBeerIdsNew = null;
  19.     ModelState.Remove("SelectedBeerIdsNew");
  20.     return View(data);
  21. }

Fíjate en el código necesario en el método que gestiona el POST. Si no establecemos SelectedBeerIdsNew a null y no eliminamos la clave SelectedBeerIdsNew del ModelState no funciona.

El resto de código es igual excepto que en la vista el Html.DropDownFor es para la propiedad SelectedBeerIdsNew (aunque iteramos sobre SelectedBeerIds):

Codigo de la vista
  1. @using WebApplication3.Controllers
  2. @model WebApplication3.Controllers.BeerSelectViewModel
  3. @{
  4.     var beers = ViewBag.Beers as IEnumerable<Beer>;
  5. }
  6.  
  7. @using (Html.BeginForm())
  8. {
  9.     foreach (var id in Model.SelectedBeerIds)
  10.     {
  11.         <p>
  12.             @Html.DropDownListFor(m => m.SelectedBeerIdsNew, new SelectList(beers, "Id", "Name", id))
  13.         </p>
  14.     }
  15.     <p>
  16.         <input type="submit" class="btn-default" value="submit" />
  17.     </p>
  18. }

Y esto es mas o menos todo… En mi opinión es un bug, porque insisto: en todo momento uso la sobrecarga de SelectList que le indica el elemento seleccionado. Si no la usase entendería el comportamiento (hasta sería lógico), pero la estoy usando. No entiendo porque no hace caso de lo que le indica el SelectList en este caso.

¿Qué opináis vosotros?

Saludos!

JavaScript – ¿Qué es exactamente this?

Si vienes de un lenguaje orientado a objetos “clásico” como C# o Java, tendrás claro el concepto de this: Se refiere al propio objeto. Dado que todos los objetos son instancias de una clase en concreto y el código se define a nivel de clase el valor de this está claramente definido. Sólo leyendo el código puedes saber a que se refiere this en cada momento.

JavaScript también tiene la palabra this pero su significado es un poco más “complejo” que el de C#.

Antes que nada mencionar que this en JavaScript NO es opcional. Siempre que quieras acceder a una propiedad de this, debes usarla. Si no usas this, JavaScript asume siempre que estás accediendo a una variable local (o global).

El valor de this en JavaScript es parecido al de C#, pero a la vez es tan distinto que lo normal es que alguien que venga de C# termine sin entender nada.

Empecemos diciendo que el valor de this dentro de una función es el valor del objeto en el que se define esta función. Veamos un ejemplo:

  1. var persona = {
  2.     name: 'edu',
  3.     twitter: 'eiximenis',
  4.     twitterUrl: 'http://twitter.com/' + this.twitter
  5. };
  6.  
  7. console.log(persona.twitterUrl);

Este código lo que imprime por pantalla es… http://twitter.com/undefined

Pero… a ver: No valía this el valor del objeto sobre el que estaba definido el código? No está twitterUrl definido dentro del objeto persona? Qué narices ocurre?

Sencillo: Si relees mi definición de this, verás que empezaba diciendo “el valor de this dentro de una función…”. La propiedad twitterUrl no es una función, así que eso no se aplica. En este caso el valor de this es el contexto en el cual se esté ejecutando este código, que por defecto es el contexto global (window en los navegadores).

Para solucionarlo, debemos convertir twitterUrl a una función:

  1. var persona = {
  2.     name: 'edu',
  3.     twitter: 'eiximenis',
  4.     twitterUrl: function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. console.log(persona.twitterUrl());

Con este cambio, ahora si que el código imprime la respuesta esperada.

Funciones constructoras

Si leíste mi post sobre si JavaScript era orientado a objetos, ya sabrás que llamo función constructora a aquella función que se usa para crear objetos que compartan un mismo prototipo. Las funciones constructoras se invocan con new y es precisamente el uso de new lo que convierte a una función en constructora, no la función en sí.

El siguiente código también muestra el valor esperado:

Code Snippet
  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. var persona = new Persona('edu','eiximenis');
  10. console.log(persona.twitterUrl());

Insisto: Es el uso de new el que hace que this sea el objeto que se está creando. ¿Que pasaría si me olvidase el new al llamar a la función Persona?

  1. var foo = Persona('edu', 'eiximenis');
  2. console.log(foo.name);
  3. console.log(name);

Ahora llamo a la función Persona pero sin usar new. Ahora el valor de this dentro de Persona ya NO es el objeto que se está creando. Ahora el valor de this dentro de persona es el valor del objeto dentro del cual la función Persona está definido… Como Persona es una función global, pues ahora this es el contexto global.

De hecho el primer console.log dará error porque foo ahora vale undefined (¡la función Persona no devuelve nada!) y el segundo console.log mostrará “edu” porque la llamada a Persona ha creado la variable “name” en el contexto global.

Recuerda: No hay clases en JavaScript. Si quieres crear objetos a través de una función constructora asegúrate de usar siempre new. Si no obtendrás resultados imprevistos. Es el uso de new el que convierte una función en constructora.

Call / Apply

Vamos a ponernos un poco más serios. Este ejemplo anterior muestra una característica fundamental de this: Su valor depende de como se llame a una función. Hemos visto que dentro de Persona el valor de this dependía de si llamábamos a la función con new o sin new.

Pero oye… ya que el valor de this dentro de una función depende de como esta sea llamada, porque no explotar al máximo esta capacidad y permitir que el valor de this sea el que el desarrollador quiera? Hasta ahora hemos visto que el valor de this podía ser:

  1. El objeto dentro del cual estaba definida la función
  2. Un objeto nuevo que se crea en aquel momento (al usar new).

Pues bien call y apply añaden un tercer escenario: el valor de this puede ser cualquier objeto que el desarrollador desee… Veamos un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return 'http://twitter.com/' + this.twitter;
  6.     }
  7. };
  8.  
  9. var p = {
  10.     blog:'http://geeks.ms/blogs/etomas'
  11. };
  12.  
  13. Persona.call(p, 'edu','eiximenis');
  14. console.log(p.twitterUrl());
  15. console.log(p.blog);

La función Persona es exactamente la misma que teníamos antes. Luego definimos un objeto p con una propiedad blog. Y en la línea 13 tenemos la clave: Usamos call para llamar a la función Persona. La función call permite especificar el valor de this dentro de una función. El primer parámetro de call es el valor que tomará this, mientras que el resto de parámetros son los que se pasan a la función.

Por lo tanto, como el primer parámetro de Persona.call es el objeto p, el valor de this dentro de Persona será el objeto p… por lo tanto las propiedades name, twitter y twitterUrl serán añadidas al objeto p.

Apply hace exactamente lo mismo que call, salvo que los parámetros que se mandan a la función se especifican en un array:

  1. Persona.apply(p, ['edu','eiximenis']);

¿P: ej. no te has preguntado nunca porque si te suscribes usando jQuery a un evento, puedes usar “this” dentro de la función gestora del evento para acceder al elemento del DOM que ha lanzado el evento? Pues ahí tienes la respuesta: Los que crearon jQuery conocen call y apply 😉

El problema de la pérdida de this

Este es un problema bastante molesto y difícil de detectar si no lo conoces y se entiende mejor con un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return (function () {
  6.             return 'http://twitter.com/' + this.twitter;
  7.         }());
  8.     }
  9. };
  10.  
  11. var p = new Persona('edu', 'eiximenis');
  12. console.log(p.twitterUrl());

El ejemplo es un pelín forzado pero ilustra la problemática de la pérdida de this.

Primero, que sepas que este código imprime http://twitter.com/undefined en lugar de http://twitter.com/eiximenis como sería de esperar. Si entiendes porque entenderás la problemática de pérdida de this.

La propiedad twitterUrl sigue siendo una función, así que… ¿porque ahora this.twitter no tiene valor? Pues porque ahora estamos accediendo a this dentro de una función que está dentro de una función. Fíjate que ahora twitterUrl es una función que define internamente a otra función anónima, la invoca y devuelve su resultado. La función interna NO está definida dentro del mismo objeto en el cual se define la propiedad twitterUrl y por lo tanto el valor de this dentro de la función interna NO es el propio objeto…  De hecho en este caso this es el contexto global. Y claro el contexto global no tiene definida ninguna propiedad twitter.

Como digo el ejemplo está muy forzado porque nadie haría eso… pero tener una función dentro de otra función es muy habitual en el caso de callbacks, así que te terminarás encontrando con este caso. Y cuando te encuentres con él… te acordarás de mi! 😛

Hay dos soluciones para esto:

  1. Hacer que el valor de this dentro de la función interna sea el correcto (a través de call/apply). Algunas librerías de javascript se encargan de ello con las funciones de callback que gestionan ellos.
  2. Guardar el valor de this en una variable.

Veamos como quedaría el código para el primer caso:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl =  function () {
  5.         return (function () {
  6.             return 'http://twitter.com/' + this.twitter;
  7.         }.apply(this));
  8.     }
  9. };
  10.  
  11. var p = new Persona('edu', 'eiximenis');
  12. console.log(p.twitterUrl());

Llamadme friki, pero a mi esto me encanta… Como puedes ver la única diferencia que hay respecto al código anterior es que ahora invocamos a la función anónima interna usando apply. Da igual que la función sea anónima y la defina justo en este lugar: puedo usar apply igualmente… Una maravilla. Si vienes de C# igual te cuesta entender esto, pero ten presente que en JavaScript las funciones son ciudadanas de primer orden. No necesitamos artificios raros como los delegates para poder pasar funciones como parámetro. Y las funciones tienen un prototipo común (de hecho apply está definido en el prototipo de Function).

El código para la segunda opción (guardar el valor de this) sería el siguiente:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4.     this.twitterUrl = function () {
  5.         var self = this;
  6.         return (function () {
  7.             return 'http://twitter.com/' + self.twitter;
  8.         }());
  9.     }
  10. };
  11.  
  12. var p = new Persona('edu', 'eiximenis');
  13. console.log(p.twitterUrl());

El código sigue siendo muy parecido con la salvedad de que ahora en la línea 5 guardamos el valor de this en una variable llamada self. En la línea 5 el valor de this es el objeto que queremos (pues estamos dentro de la funcion twitterUrl y no dentro de la función interna). Y dentro de la función interna (línea 7) no usamos this si no la variable anterior (self). Es una convención usar los nombres that o self para esta variable que guarda el valor de this.

Bind

Bueno… vamos a ponernos ya en plan superlativo. Si creías que ya lo habíamos visto todo sobre JavaScript y this, todavía nos queda una sorpresa más: La función bind.

Bind es muy similar a call o apply, pero en lugar de devolver el resultado de la función que se llama, devuelve… otra función. ¿Y qué es esta otra función? Pues es la función original pero con el valor this por defecto establecido siempre al valor del parámetro pasado a bind.

Veamos un ejemplo:

  1. var Persona = function (n, tw) {
  2.     this.name= n;
  3.     this.twitter = tw;
  4. };
  5.  
  6. function getTwitterUri() {
  7.     return 'http://twitter.com/' + this.twitter;
  8. }
  9.  
  10. var p = new Persona('edu', 'eiximenis');
  11. console.log(getTwitterUri.apply(p));
  12.  
  13. var ftw = getTwitterUri.bind(p);
  14. console.log(ftw());

¿Cual es la salida de este código? ¡Exacto! Se imprime dos veces ‘http://twitter.com/eiximenis’:

  1. La primera vez es porque usamos apply para invocar a la función getTwitterUri con el objeto p, por lo que el valor de this dentro de getTwitterUri es el objeto p.
  2. La segunda es porque invocamos la funcion ftw. La función ftw es devuelta por la llamada a getTwitterUri.bind por lo que al invocar la función ftw el valor de this es el propio objeto p.

Ahora añadamos las siguientes dos líneas más de código:

  1. console.log(getTwitterUri.apply({ twitter: 'msdev_es' }));
  2. console.log(ftw.apply({ twitter: 'msdev_es' }));

¿Qué crees que imprimen esas dos líneas de código adicionales?

Si has respondido que ambas van a imprimir ‘http://twitter.com/msdev_es’ déjame darte una buena noticia y una mala:

  • La buena es que has entendido como funcionan call y apply! 🙂
  • La mala es que la respuesta no es correcta 😛

La primera línea SI que imprime ‘http://twitter.com/msdev_es’, ya que llamamos a getTwitterUri usando apply y pasándole un objeto cuya propiedad twitter es ‘msdev_es’…

… Pero la segunda línea NO imprime ‘http://twitter.com/msdev_es’. Debería hacerlo si ftw fuese una funcion “normal”. Pero ftw es una función obtenida a través de bind, por lo que sigue conservando su valor de this con independencia de call y apply.

Así que ya ves… Para saber el valor de this que usa una función ya ni te basta con mirar si es invocada usando call y apply: debes además saber si esta función ha sido generada a través de bind o no 😛

En fin… Creo que más o menos con esto cubrimos todos los aspectos de this en JavaScript. Como puedes ver, aunque su significado es parecido al de C# realmente es tan distinto que lo más normal es que si asumes que el this de JavaScript funciona como el de C# termines por no entender nada y maldecir a JavaScript… 😉

Un saludo! 😉

¿Es Javascript orientado a objetos?

Ayer tuve el placer de participar en un hangout de #JsIO junto a dos bestias pardas como Erick Ruiz y Tomás Corral discutiendo si JavaScript es o no es un lenguaje orientado a objetos.

Así que aprovecho para escribir este post y hacer algunas reflexiones más al respecto sin las prisas ni la improvisación del “directo”.

¿Qué es la orientación a objetos?

Es complicado definir que significa exactamente “orientación a objetos”. Seguro que si le preguntas a varia gente distinta obtendrás diferentes respuestas. Con puntos coincidentes, pero también puntos divergentes. Esto se debe a que el concepto es lo suficientemente vago como para admitir varias interpretaciones y además de que la gran mayoría de lenguajes no son 100% orientados a objetos.

Si yo hubiese de definir orientación a objetos diría básicamente que se trata de modelar el problema, y por lo tanto de diseñar un programa, basándose en la interacción entre distintos objetos que tienen determinadas propiedades y con los cuales se pueden realizar determinadas acciones. Los objetos tienen tres propiedades fundamentales:

  1. Comportamiento: Se define como “lo que es posible hacer con un objeto”. En terminología más purista, el conjunto de mensajes a los que este objeto responde (en OOP purista se usa la terminología mensaje para referirse al paso de información entre objetos).
  2. Estado: Los objetos tienen un estado en todo momento, definido por el conjunto de las variables o campos que contienen. El estado es pues la información contenida en un objeto.
  3. Identidad: Cada objeto existe independientemente del resto. Pueden haber dos objetos iguales pero no tienen porque ser el mismo (de igual forma que dos mellizos pueden ser idénticos, pero no por ello dejan de ser dos personas).

No iría mucho más allá, porque para mi esta definición contiene la clave que diferencia de la POO de la programación procedural: Los datos (estado) y funciones relacionadas (comportamiento) están agrupados en una sola entidad (objeto), en lugar de estar dispersos en el código. Es decir, el objeto encapsula los datos y el comportamiento. Como desarrollador debemos pensar en modelar nuestro sistema como un conjunto de objetos, en lugar de como un conjunto de funciones (procedimientos) invocadas una tras otra y pasándose datos más o menos arbitrarios para solucionar el problema.

El resto son detalles: herencia, polimorfismo, visibilidad… son detalles. No son menores, ciertamente, pero desde un punto de visto teórico, son meros detalles.

Y fíjate: he sido capaz de definir POO sin usar la palabra clase una sola vez. Y es que las clases son un mecanismo para implementar la POO, pero nada más. Y por supuesto, no son el único.

JavaScript tiene objetos, y estos objetos tienen comportamiento, estado e identidad. Así que desde punto de vista… Es orientado a objetos.

Pero como entiendo que esta respuesta puede no colmar a todos, especialmente a aquellos que vengan de Java, C# o algún otro lenguaje orientado a objetos “tradicional” (debería decir basado en clases) vayamos a ver como en JavaScript también tenemos esos detalles que mencionaba antes disponibles… 😉

¿Herencia en JavaScript?

Vale… Antes que nada: Si has aprendido POO con Java, C#, C++ o algún otro lenguaje basado en clases recuerda el concepto fundamental: No hay clases en JavaScript. Por lo tanto, hazte un favor y deja de pensar en clases cuando desarrolles en JavaScript. Te ahorrarás muchos dolores de cabeza.

La herencia en JavaScript es herencia entre objetos.

  • En un lenguaje basado en clases, la herencia es una relación definida entre clases. Si una clase Rectángulo deriva de la clase Figura, todos los objetos Rectángulo heredarán todo el comportamiento y estado definidos en la clase Figura.
  • En JavaScript la herencia es una relación definida entre objetos. Si un objeto deriva de otro, heredará todo el comportamiento y estado definido en el objeto base.

El mecanismo de herencia que se usa en JavaScript se conoce como herencia por prototipo y para resumirlo diré básicamente que cada objeto tiene asociado un prototipo. Si el desarrollador invoca un método sobre un objeto y este método NO está definido en el propio objeto, el motor de JavaScript buscará el método en el prototipo de este objeto. Por supuesto el prototipo es un objeto y puede tener otro prototipo y así sucesivamente, creando lo que se conoce como cadena de prototipado. La pregunta es pues como crear y asignar el prototipo de un objeto. Hay varias maneras de hacerlo, pero veamos una rápida y sencilla:

Herencia por prototipo (1)
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw();

Si ejecutas este código, la salida por la consola es “figura::draw”. La clave es el método Object.create. Este método crea un objeto vacío cuyo prototipo es el objeto pasado por parámetro. Esta es una forma sencilla de tener herencia en JavaScript.

Igual te preguntas si el objeto rect puede sobreescribir la implementación de draw (si vienes de Java sabrás que una clase hija puede redefinir los métodos de su clase base; si vienes de C# sabrás eso mismo siempre que los métodos sean virtuales). La respuesta es claro que sí:

Redefinicion de metodos
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw = function() {
  8.     console.log("rect::draw");
  9. };
  10. rect.draw();

Ahora la salida por pantalla es “rect::draw”. ¿Qué ha sucedido? Pues como hemos llamado al método draw del objeto rect y este ya lo tiene definido, JavaScript ya lo invoca y no lo busca por la cadena de prototipos.

¿Lo ves? Herencia sin clases. ¡Es posible! 😉

¿Polimorfismo en JavaScript?

Una de las preguntas que salieron en el hangout fue si había polimorfismo en JavaScript. Respondí diciendo que esta pregunta implica una forma de pensar “basada en clases” pero que sí que era posible simular el polimorfismo en JavaScript.

Pero es que una de las claves es que no es necesario el concepto de polimorfismo en JavaScript. Antes que nada aclaremos que es el polimorfismo: La posibilidad de enviar un mismo mensaje (es decir, de invocar a un método) a varios objetos de naturaleza homogénea (wikipedia). Me gusta especialmente esta definición porque define polimorfismo sin usar la palabra “clase”. Si habéis aprendido POO con un lenguaje basado en clases quizá tenéis una definición de polimorfismo redactada de forma ligeramente distinta, algo como: Que los objetos de una clase derivada pueden tratarse como objetos de la clase base, pero manteniendo su comportamiento distintivo.

Supón que estás en un lenguaje basado en clases, como C#, y tienes la clase Figura y su derivada la clase Rect. La clase Figura define un método virtual llamado Draw() que es redefinido en la clase Rect. El polimorfismo implica que puedo pasar un objeto de la clase Rect a un método que acepte como parámetro un objeto de la clase Figura. Y que si este método llama al método Draw del parámetro Figura que ha recibido… se ejecutará el método Draw de la clase Rect, porque aunque el parámetro es de tipo Figura, el objeto real es de tipo Rect.

¿Y en JavaScript? Veamos:

Polimorfismo
  1. var figura = {
  2.     draw: function() {
  3.         console.log("figura::draw");
  4.     }
  5. };
  6. var rect = Object.create(figura);
  7. rect.draw = function() {
  8.     console.log("rect::draw");
  9. };
  10.  
  11. function Polimorfismo(figura) {
  12.     figura.draw();
  13. }
  14.  
  15. Polimorfismo(rect);

La función Polimorfismo llama al método draw() del objeto que recibe. Como lo paso el objeto rect lo que se ejecuta es el método draw del objeto rect. Por lo tanto… tenemos polimorfismo automático en JavaScript. De hecho JavaScript tiene una característica conocida como Duck Typing, que bueno… es dificil de definir, pero una manera de hacerlo es: “Si camina como un pato, grazna como un pato y luce como un pato… es un pato”. O dicho de otro modo: Para el método Polimorfismo lo único que importa del parámetro figura es que tenga un método draw(). Nada más. Por lo tanto cualquier objeto que tenga un método draw es válido para ser usado como parámetro del método Polimorfismo.

El Duck Typing se suele asociar a los lenguajes dinámicos… pero no es exclusivo de ellos. Java y C# (sin usar dynamic) no tienen Duck Typing, pero p. ej. C++ lo ofrece a través de los templates.

¿Clases en JavaScript?

A esta altura del post ya debes tener claro que no. JavaScript no tiene clases, solo objetos y relaciones entre objetos. Pero existe un método de crear objetos a través de una función constructora y la palabra clave new que se parece mucho a como se hace en un lenguaje basado en clases. Eso que a priori es buena idea, ha ayudado mucho a la confusión de JavaScript… ¿Uso new para crear objetos pero no hay clases? ¿Y eso?

Déjame mostrarte un trozo de código de como crear un objeto basado en función constructora y el uso de new:

Funciones constructoras y new
  1. var Figura = function() {
  2.     this.draw = function() {
  3.         console.log("Figura::draw");
  4.     };
  5. };
  6.  
  7. var figura = new Figura();
  8. var rect = Object.create(figura);
  9.  
  10. rect.draw();

Ahora la variable Figura no es nada más que una función. Pero un tipo especial de función que llamamos función constructora. Pero ¡ojo! lo que convierte Figura en una función constructora no es nada que defina la propia función. Es como se invoca. Es el hecho de usar new lo que convierte Figura en una función constructora. El uso de new implica varias cosillas, cuyo ámbito se escapa de este post, pero la norma básica es que es la forma para invocar funciones cuya funcionalidad es crear objetos. En este ejemplo pues tengo:

  • Figura: Función que sirve para construir objetos que tienen un método draw.
  • figura (con la f minúscula): Objeto creado a partir de Figura
  • rect: Objeto cuyo prototipo es el objeto figura.

Lo interesante de usar funciones constructoras es que todos los objetos creados a través de ellas comparten el mismo prototipo. Si tenemos:

  1. var figura = new Figura();
  2. var figura2 = new Figura();

Ambos objetos comparten el mismo prototipo. Si ahora quiero añadir un método, p.ej. clear() que esté disponible para todos los objetos creados a partir de la función constructora Figura puedo añadirlo al prototipo. Y como hago esto? Pues así:

  1. var Figura = function() {
  2.     this.draw = function() {
  3.         console.log("Figura::draw");
  4.     };
  5. };
  6.  
  7. var figura = new Figura();
  8. var figura2 = new Figura();
  9.  
  10. Figura.prototype.clear = function() {
  11.     console.log("Figura::clear");
  12. };
  13.  
  14. var rect = Object.create(figura);
  15.  
  16. figura.clear();
  17. figura2.clear();
  18. rect.clear();

Figura.prototype es el nombre del prototipo de todos los objetos creados a través de new Figura. Este codigo imprime tres veces “Figura::clear”. Fíjate que funciona incluso cuando el método clear ha sido añadido al prototipo después de crear figura y figura2. Y es interesante el caso del objeto rect. Que ocurre cuando hacemos rect.clear()?

  • JavaScript busca el método clear() en el objeto rect. No lo encuentra y
  • Busca el método en el prototipo de rect que es figura. No lo encuentra y
  • Busca el método en el prototipo de figura que es Figura.prototype. Lo encuentra y lo ejecuta.

Aquí tienes a la cadena de prototipado en acción.

Fíjate que seguimos teniendo tan solo objetos. Figura no es una clase. Figura es una función. Una función para crear objetos que comparten un prototipo. Nada más.

¿Variables privadas en JavaScript?

JavaScript no incorpora de serie ningún mecanismo de visibilidad para los miembros de un objeto. Todo es público por defecto. Pero por supuesto no hay nada que no podamos conseguir… 🙂

Visibilidades en JavaScript
  1. var Figura = function (c, stroke) {
  2.     // Variable privada
  3.     var _color = c;
  4.     // Variable pblica
  5.     this.stroke = null;
  6.     // Necesario para poder acceder
  7.     // a this desde los mtodos privados
  8.     var self = this;
  9.     // Mtodo privado
  10.     var _setup = function(s) {
  11.         // Ah podemos acceder a mtodos pblicos
  12.         // a travs de self. Y a los privados directamente
  13.         self.stroke = s;
  14.     };
  15.     _setup(stroke);
  16.     // Mtodos pblicos
  17.     this.draw = function() {
  18.         console.log("Figura::draw in color " + _color  + " and stroke " + this.stroke );
  19.     };
  20.     this.getColor = function() {
  21.         return _color;
  22.     };
  23. };
  24.  
  25.  
  26. var f = new Figura("red", "thin");
  27. console.log(f._color); // undefined
  28. console.log(f.getColor()); // red
  29. console.log(f.stroke); // thin
  30. //f._setup("thick");  // Error: Object has no method _setup
  31.  
  32. var f2 = new Figura("blue","thick");
  33. console.log(f2._color); // undefined
  34. console.log(f2.getColor()); // blue
  35. console.log(f2.stroke); // thick
  36. //f._setup("thick");  // Error: Object has no method _setup
  37. f.draw();
  38. f2.draw();

Este ejemplo muestra como definir métodos y variables privadas. ¡Hay otras técnicas y alternativas!

Básicamente la regla es:

  • Usar la técnica de función constructora
  • Los métodos/variables privados son funciones o variables declaradas dentro de la función constructora.
  • Los métodos/variables públicas se asignan a this.
  • Guardar el valor de this en una variable privada (usualmente se usa that o self).
  • Desde las funciones privadas debes usar self para acceder a las variables públicas.
  • Desde las funciones públicas puedes usar self o this para acceder a las variables públicas.
  • En ambos casos puedes acceder a las variables privadas directamente con su nombre.

¿Herencia múltiple en JavaScript?

Si vienes de C# o Java igual alzas una ceja ahora… ¿No era peligrosa la herencia múltiple? C# y Java no incorporan este concepto debido a su posible peligrosidad y porque muchas veces da más problemas de los que puede solucionar. El peligro de la herencia múltiple es conocido como el problema de la herencia en diamante. Todos los lenguajes que soportan herencia múltiple se deben enfrentar a este problema, así que para evitarlo algunos lenguajes como Java o C# han decidido prescindir de ella.

JavaScript no soporta por defecto herencia múltiple pero si que soporta un tipo especial de herencia múltiple llamada mixin. Con los mixins ya entramos en un terreno pantanoso si vienes de C# o Java puesto que estos dos lenguajes no soportan este concepto.

Resumiendo, un Mixin es una clase que está pensada para ser incorporada dentro de otra clase ofreciendo funcionalidad adicional. Es como si tuvieras 3 clases A, B y C y las “combinases” todas ellas en una clase D (que además añadiría su propia funcionalidad). Algunos lenguajes como Lisp o Python soportan Mixins nativamente. Otros como C++ no, pero pueden imitarlos fácilmente debido al soporte de herencia múltiple (del que el uso de mixins es un tipo específico). Usar mixins en Java o C# es realmente complicado (aunque en Java8 el uso de default methods en interfaces lo hace posible). Si estás interesado en Mixins y C# echa un vistazo a heredar de José F. Romaniello.

¿Y como usar Mixins en C#? Aquí tienes un ejemplo:

Mixins
  1. var asCircle = function() {
  2.     this.area = function() {
  3.         return Math.PI * this.radius * this.radius;
  4.     };
  5.  
  6.     return this;
  7. };
  8.  
  9. var asButton = function() {
  10.     this.click = function() {
  11.         console.log("Button has been clicked");
  12.     };
  13.     return this;
  14. };
  15.  
  16. var a = { radius: 10 };
  17. asCircle.call(a);
  18. asButton.call(a);
  19. console.log(a.area());
  20. console.log(a.click());

En este cas asCircle y asButton son los dos Mixins. El primero añade una funcionalidad area a todos los objetos que tengan una propiedad llamada radius. El segundo añade un método click.

Para aplicar los Mixins sobre un objeto usamos la función call. No entraré en detalles de call ahora porque excede el objetivo de este post. Pero la clave está en que después de aplicar los Mixins, el objeto a tiene los métodos area y click.

Y más o menos… ¿con esto podemos dar por terminado el post no? Espero, que esto os haya ayudado a entender un poco mejor la POO bajo JavaScript y que bueno… os hayáis convencido de que JavaScript no tendrá clases pero orientado a objetos es 🙂

Saludos!

PD: Os paso el enlace del video de youtube donde podeis ver el hangout: http://www.desarrolloweb.com/en-directo/particularidades-programacion-orientada-objetos-poo-devio-8452.html

Divertimento: Cadenas de longitud máxima fija en C#

Aviso: Este post es un divertimento que ha surgido a raíz del siguiente tweet de Juan Quijano. En este tweet básicamente Juan preguntaba si había alguna manera de limitar la longitud de una cadena. Por supuesto todas las respuestas que le dan son correctísimas, a saber:

  1. Validarlo en el setter
  2. Usar DataAnnotations y validación con atributos
  3. Usar [StringLength] en el caso de ASP.NET MVC
  4. Y otras que se podrían dar aquí.

Pero me he preguntado cuan sencillo sería crear en C# una clase cadena de longitud fija pero que se comportase como una cadena. Es decir que desde el punto de vista del usuario no haya diferencia entre objetos de esta clase y las cadenas estándar.

En este post os cuento a la solución a la que he llegado, que no tiene porque ser la única ni la mejor, y los “problemas” que me he encontrado. Además la solución me da una excusa para contar una capacidad de C# que mucha gente no conoce ni usa que son las conversiones implícitas personalizadas 🙂

Conversiones implícitas personalizadas

Las conversiones implícitas personalizadas son uno de los dos puntos clave de la solución a la que he llegado (el otro son los genéricos).

Una conversión implícita personalizada es la capacidad de los valores de un tipo para convertirse automáticamente en valores de otro tipo cuando es necesario. P. ej. hay una conversión implícita de int a float:

  1. float f = 10;

En esta línea la constante 10 es de tipo int, pero en cambio f es de tipo float. La asignación funciona no porque 10 sea un float si no porque hay una conversión implícita entre int (10) y float.

En cambio no hay una conversión implícita de double a float y es por ello que esa línea no compila:

  1. float f = 100.0;

Esta línea no compila porque las constantes decimales son de tipo double. Y aunque 100.0 es un valor válido para un float (pues entra dentro de su rango y capacidad), para el compilador es un double y no puede transformar un double en un float porque no hay una transformación implícita.

Por supuesto podemos asignar 100.0 a un float, usando este código:

  1. float f = (float)100.0;

Ahora estamos usando una conversión explícita. Y dado que hay definida una conversión explícita entre double (100.0) y float, el código compila. Que la conversión sea explícita tiene su lógica: la conversión de double a float puede generar una pérdida de rango y/o precisión. Por ello no hay una conversión implícita (que sucedería automáticamente y podría generar errores). Por ello la conversión es explícita, obligando al desarrollador a indicar (mediante el casting) que quiere realizarla y que es consciente de los peligros que pueda haber.

El operador de casting en C# pues invoca a una conversión explícita, que debe estar definida. P. ej. el siguiente código no compila, por más que usemos el casting:

  1. float f = (float) "100.0";

Y no compila porque NO hay definida ninguna conversión explícita de string (“100.0”) a float.

Pues bien, en C# una clase puede definir conversiones explícitas e implícitas desde y a otros tipos.

Yo empecé mi solución con una clase muy simple: Una clase que contuviera nada más que una cadena pero que se convirtiese implícitamente desde y a string:

Code Snippet
  1. sealed class FixedLengthString
  2. {
  3.     private string _buffer;
  4.  
  5.     public FixedLengthString()
  6.     {
  7.         _buffer = string.Empty;
  8.     }
  9.  
  10.     public FixedLengthString(string initialValue)
  11.     {
  12.         _buffer = initialValue;
  13.     }
  14.  
  15.  
  16.     public override string ToString()
  17.     {
  18.         return _buffer;
  19.     }
  20.  
  21.     public static implicit operator string(FixedLengthString value)
  22.     {
  23.         return value._buffer;
  24.     }
  25.  
  26.     public static implicit operator FixedLengthString(string value)
  27.     {
  28.         return new FixedLengthString(value);
  29.     }
  30.  
  31.     public override bool Equals(object obj)
  32.     {
  33.         if (obj == null)
  34.         {
  35.             return false;
  36.         }
  37.  
  38.         if (obj is string)
  39.         {
  40.             return obj.Equals(_buffer);
  41.         }
  42.  
  43.         return (obj is FixedLengthString) ?
  44.             ((FixedLengthString)obj)._buffer == _buffer :
  45.             base.Equals(obj);
  46.     }
  47.  
  48.     public override int GetHashCode()
  49.     {
  50.         return (_buffer ?? string.Empty).GetHashCode();
  51.     }
  52. }

Fíjate que esta clase no es nada más que un contenedor para una cadena (_buffer), pero la clave está en los dos métodos estáticos:

Conversiones Implicitas
  1. public static implicit operator string(FixedLengthString value)
  2. {
  3.     return value._buffer;
  4. }
  5.  
  6. public static implicit operator FixedLengthString(string value)
  7. {
  8.     return new FixedLengthString(value);
  9. }

El primero de los dos define la conversion de FixedLengthString a cadena y el segundo la conversión desde cadena a FixedLengthString.

Fíjate la sintaxis:

  • El método es static
  • Se usa implicit operator para indicar que es una conversión implícita (usaríamos explicit operator para indicar una de explícita).
  • Como valor de retorno colocamos el del tipo al que nos convertimos
  • Recibimos un parámetro del tipo desde el que nos convertimos.

Gracias a estas conversiones implícitas, el siguiente código es válido:

  1. FixedLengthString str = "YYY";
  2. Console.WriteLine(str);

En la primera línea estamos invocando la conversión implícita de cadena (“YYY”) a FixedLengthString y en la segunda la conversión contraria (Console.WriteLine espera un parámetro de tipo string y str es un FixedLengthString).

Bien, ahora tenía una clase que envolvía una cadena y que para el desarrollador se comportaba como una cadena. Sólo había que añadir la longitud máxima y listos.

Pero no era tan fácil.

La primera solución que se nos puede ocurrir pasa por declarar un campo con la longitud máxima de la cadena y en el constructor de FixedLengthString pasar que longitud máxima queremos. Crear la clase FixedLengthString para que contenga cadenas de como máximo una longitud determinada es fácil y no tiene ningún secreto. El problema está en mantener las conversiones implícitas, especialmente la conversión implícita desde una cadena hacia una FixedLengthString.

Supongamos que definimos la clase FixedLengthString para que acepte un parámetro en el constructor que defina la longitud máxima. Entonces podríamos declarar una variable para contener el DNI así:

  1. var dni = new FixedLengthString(9);

Ahora si usáramos métodos definidos en la clase (p. ej. supongamos que la clase FixedLengthString definiese un método SetValue o algo así) podríamos controlar fácilmente que el valor del buffer interno no excediese nunca de 9 carácteres. Pero yo no quería eso: yo quería que la clase se pudiese usar como una cadena estándar se tratase. Es decir poder hacer:

  1. dni = "12345678A";

En esta línea se invoca la conversión implícita desde cadena hacia FixedLengthString… ¿Y cual es el problema? Que es estática. Mira de nuevo el código de dicha conversion:

  1. public static implicit operator FixedLengthString(string value)
  2. {
  3.     return new FixedLengthString(value);
  4. }

Dentro de la conversión no puedo saber cual es el valor de la longitud máxima porque la conversión es estática y el valor de la longitud máxima está en un campo de instancia (el valor de longitud máxima puede ser distinto en cada objeto). Hablando claro: La conversión implícita devuelve un nuevo objeto, y NO puede acceder a las propiedades del objeto anterior si lo hubiese (en mi caso el objeto anterior guardado en dni).

Parece que estamos en un callejon sin salida…

En este punto he empezado un proceso de pensamiento que ha discurrido más o menos así:

  1. El problema es que el campo de longitud máxima es un campo de objeto (no estático) y la conversión es estática.
  2. Entonces si guardo el campo de longitud máxima en una variable estática, podré acceder a dicho valor en la conversión…
  3. … Aunque claro, este enfoque tiene un problema: El valor de longitud máxima es compartido por todos los objetos de la clase. No puedo tener un objeto FixedLengthString de longitud máxima 9 (para un DNI p. ej.) y otro de longitud máxima 5 (p. ej. para un código postal).

Evidentemente el punto 3, parece descartar la idea pero… ¿Y si pudiésemos tener varias clases distintas pero todas con el mismo código, pero tan solo cambiando el valor de longitud máxima? Entonces… ¡todo funcionaría!

Y… qué mecanismo conocéis en C# que permite generar clases distintas con el mismo código? Exacto: Los genéricos.

Si había una solución pasaba por usar genéricos.

Genéricos al rescate

Pero había un pequeño temilla: el parámetro que yo quería generalizar era el valor de longitud máxima, que es un int y esto no está permitido en genéricos. En los templates de C++ (que vienen a ser como los genéricos de .NET pero hipervitaminados) es posible generalizar parámetros de un tipo específico, pero en .NET no. En .NET los parámetros de los genéricos definen siempre un tipo, no un valor. P. ej. en el genérico List<T> el parámetro T es siempre un tipo (si T vale int tienes List<int> y si T vale string tienes List<string>). Y lo mismo ocurre en cualquier genérico que definas en .NET.

En fin… no era perfecto pero ya tenía la idea montada en mi mente. No era perfecta porque me obligaba a crear un tipo específico, distinto, por cada valor de longitud máxima que quisiera, pero bueno, al menos serían tipos muy sencillos. De hecho, serían tipos vacíos, tan solo decorados con un atributo.

Empecé definiendo el atributo que usaría:

FixedLengthMaxAttribute
  1. public class FixedStringLengthMaxAttribute : Attribute
  2. {
  3.     public int Length { get; set; }
  4.  
  5.     public FixedStringLengthMaxAttribute(int len)
  6.     {
  7.         this.Length = len;
  8.     }
  9. }

La idea era la siguiente: Por cada valor de longitud máxima que se quisiera se crea una clase vacía y se decora con este atributo con el valor máximo deseado.

Luego se crea una instancia de la clase FixedLengthString<T> y se pasa como valor del tipo genérico T la clase creada y decorada con el atributo. Para declarar un DNI de 9 carácteres sería:

  1. [FixedStringLengthMaxAttribute(9)]
  2. internal class NifSize
  3. {
  4. }

  1. var nif = new FixedLengthString<NifSize>();

Una vez tenemos el objeto nif podemos convertirlo desde y a cadena sin ningún problema (como hemos visto antes) y se mantiene la longitud máxima de 9 carácteres (en mi implementación se trunca si la cadena desde la que convertimos es más larga).

Ah si… Y falta la implementación de la clase FixedLenghString<T>. Básicamente es la misma que la original FixedLengthString pero con un constructor estático que lee via reflection el atributo [FixedStringLength] aplicado al tipo T y guarda el valor de la propiedad Length de este atributo en un campo estático:

FixedLengthString<T>
  1. sealed class FixedLengthString<T>
  2. {
  3.     private string _buffer;
  4.     private static int _maxlen;
  5.  
  6.     static FixedLengthString()
  7.     {
  8.         var type = typeof (T);
  9.         var attr = type.GetCustomAttribute<FixedStringLengthMaxAttribute>();
  10.         if (attr == null)
  11.         {
  12.             _maxlen = Int32.MaxValue;
  13.         }
  14.         else
  15.         {
  16.             _maxlen = attr.Length;
  17.         }
  18.     }
  19.  
  20.  
  21.     public FixedLengthString()
  22.     {
  23.         _buffer = string.Empty;
  24.     }
  25.  
  26.     public FixedLengthString(string initialValue)
  27.     {
  28.         _buffer = initialValue.Length < _maxlen ? initialValue : initialValue.Substring(0, _maxlen);
  29.     }
  30.  
  31.      
  32.     public override string ToString()
  33.     {
  34.         return _buffer;
  35.     }
  36.  
  37.     public static implicit operator string(FixedLengthString<T> value)
  38.     {
  39.         return value._buffer;
  40.     }
  41.  
  42.     public static implicit operator FixedLengthString<T>(string value)
  43.     {
  44.         return new FixedLengthString<T>(value);
  45.     }
  46.  
  47.     public override bool Equals(object obj)
  48.     {
  49.         if (obj == null)
  50.         {
  51.             return false;
  52.         }
  53.  
  54.         if (obj is string)
  55.         {
  56.             return obj.Equals(_buffer);
  57.         }
  58.  
  59.         return (obj is FixedLengthString<T>) ?
  60.             ((FixedLengthString<T>)obj)._buffer == _buffer :
  61.             base.Equals(obj);
  62.     }
  63.  
  64.     public override int GetHashCode()
  65.     {
  66.  
  67.         return (_buffer ?? string.Empty).GetHashCode();
  68.     }
  69. }

¡Y listos!

Por supuesto puedo crear una clase con una propiedad FixedLengthString<T>:

  1. class Persona
  2. {
  3.     public string Nombre { get; set; }
  4.     public FixedLengthString<NifSize> NIF { get; set; }
  5.     public Persona()
  6.     {
  7.         NIF = new FixedLengthString<NifSize>();
  8.     }
  9. }

Y operar con el NIF de esas personas como si fuesen cadenas normales:

  1. Persona p = new Persona();
  2. Console.WriteLine(p.NIF);
  3. p.NIF = "12345678A";
  4. Console.WriteLine(p.NIF);
  5. p.NIF = "1234567890987654321Z";
  6. Console.WriteLine(p.NIF);

La salida de este programa es:

image

Se puede observar como la cadena se trunca a 9 caracteres.

Bueno… Llegamos al final de este post, espero que os haya resultado interesante. Por supuesto no digo que esta sea la solución para cadenas de tamaño máximo, si no que como he dicho al principio es un simple divertimento 😉

Saludos!

ASP.NET MVC – Custom model binders por propiedad

Muy buenas! En este post vamos a ver como habilitar un custom model binder para una propiedad en concreto de un viewmodel.

De serie es posible configurar un Custom Model Binder de dos maneras:

  1. Añadiéndolo a la colección Binders de la clase ModelBinders. Con esto conseguimos que todos los valores de un tipo concreto se enlacen usando nuestro ModelBinder.
  2. Usando el atributo [ModelBinder]. Con este atributo podemos especificar un Model Binder a usar para un viewmodel en concreto o para un parámetro en concreto de un controlador. Pero desgraciadamente no para una propiedad concreta de un viewmodel:

image

Por suerte conseguir usar un custom model binder para una propiedad en concreto de un viewmodel no es tan complicado 🙂

Lo primero es crearnos un atributo propio ya que el ModelBinderAttribute no podemos usarlo, así que vamos a ello:

Atributo [PropertyModelBinder]
  1. AttributeUsage(AttributeTargets.Property)]
  2.     public class PropertyModelBinderAttribute : CustomModelBinderAttribute
  3.     {
  4.         private readonly Type _typeToUse;
  5.  
  6.         public PropertyModelBinderAttribute(Type typeToUse)
  7.         {
  8.             _typeToUse = typeToUse;
  9.         }
  10.  
  11.         public override IModelBinder GetBinder()
  12.         {
  13.             var binder = DependencyResolver.Current.GetService(_typeToUse) as IModelBinder;
  14.             return binder ?? new DefaultModelBinder();
  15.         }
  16.     }

Una vez lo tenemos creado, lo aplicamos a nuestro viewmodel:

ViewModel de prueba
  1. public class SomeViewModel
  2. {
  3.     [PropertyModelBinder(typeof(RomanNumberModelBinder))]
  4.     [Range(1,21)]
  5.     public int Century { get; set; }
  6.     public string Name { get; set; }
  7. }

Ahora debemos implementar el custom model binder. En este caso he implementado un model binder que convierte cadenas en formato de número romano (tal como XX o VIII) al entero correspondiente.

Nota: El código de conversión lo he sacado de http://vadivel.blogspot.com.es/2011/09/how-to-convert-roman-numerals-to.html. Tan solo lo he corregido para usar un SortedDictionary y así asegurarme de que el orden por el que se itera sobre las claves es el esperado (en un Dictionary<,> el orden de iteración no está definido).

El código entero de mi ModelBinder es el siguiente:

RomanNumberModelBinder
  1. public class RomanNumberModelBinder : IModelBinder
  2. {
  3.     private static List<string> _romanTokens = new List<string>
  4.     {
  5.         "M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I",
  6.     };
  7.  
  8.     private class RomanKeyComparer : IComparer<string>
  9.     {
  10.         public int Compare(string x, string y)
  11.         {
  12.  
  13.             var idx = _romanTokens.IndexOf(x);
  14.             var idx2 = _romanTokens.IndexOf(y);
  15.             if (idx == idx2) return 0;
  16.             return idx < idx2 ? 1 : 1;
  17.  
  18.         }
  19.     }
  20.  
  21.     private static SortedDictionary<string, int> RomanNumbers =
  22.         new SortedDictionary<string, int>(new RomanKeyComparer());
  23.  
  24.     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  25.     {
  26.         if (bindingContext.ModelType != typeof (int)) return null;
  27.         var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
  28.  
  29.         return RomanToInt(value);
  30.     }
  31.  
  32.     private static int RomanToInt(string roman)
  33.     {
  34.         if (!RomanNumbers.Any())
  35.         {
  36.             RomanNumbers.Add(_romanTokens[0], 1000);
  37.             RomanNumbers.Add(_romanTokens[1], 900);
  38.             RomanNumbers.Add(_romanTokens[2], 500);
  39.             RomanNumbers.Add(_romanTokens[3], 400);
  40.             RomanNumbers.Add(_romanTokens[4], 100);
  41.             RomanNumbers.Add(_romanTokens[5], 90);
  42.             RomanNumbers.Add(_romanTokens[6], 50);
  43.             RomanNumbers.Add(_romanTokens[7], 40);
  44.             RomanNumbers.Add(_romanTokens[8], 10);
  45.             RomanNumbers.Add(_romanTokens[9], 9);
  46.             RomanNumbers.Add(_romanTokens[10], 5);
  47.             RomanNumbers.Add(_romanTokens[11], 4);
  48.             RomanNumbers.Add(_romanTokens[12], 1);
  49.         }
  50.         int result = 0;
  51.  
  52.  
  53.         foreach (var pair in RomanNumbers)
  54.         {
  55.             while (roman.IndexOf(pair.Key.ToString()) == 0)
  56.             {
  57.                 result += int.Parse(pair.Value.ToString());
  58.                 roman = roman.Substring(pair.Key.ToString().Length);
  59.             }
  60.         }
  61.         return result;
  62.     }
  63. }

Teóricamente lo tenemos todo montado. Pero si lo probamos veremos que NO funciona. Para la prueba me he generado una pequeña vista como la siguiente:

Vista de prueba
  1. @model WebApplication1.Models.SomeViewModel
  2.  
  3. @using (Html.BeginForm())
  4. {
  5.     <p>
  6.         @Html.LabelFor(m => m.Name)
  7.         @Html.EditorFor(m => m.Name)
  8.     </p>
  9.     <p>
  10.         @Html.LabelFor(m => m.Century)
  11.         @Html.TextBoxFor(m => m.Century, new {placeholder = "In Romna numbers, like XX or XIX"})
  12.     </p>
  13.     
  14.     <input type="submit" value="send"/>
  15. }

Y luego las acciones correspondientes en el controlador para mostrar la vista y recibir los resultados. Si lo probáis veréis que nuestro RomanNumberModelBinder no se invoca 🙁

El “culpable” de que no funcione es el ModelBinder por defecto (DefaultModelBinder). Dado que el atributo [ModelBinder] origina no puede aplicarse a propiedades, el DefaultModelBinder no tiene en cuenta la posibilidad de que una propiedad en concreto use un model binder distinto. Así pues nos toca reescribir parte del DefaultModelBinder.

Para reescribir parte del ModelBinder lo más sencillo es heredar de él y redefinir el método que necesitemos. En este caso el método necesario es BindProperty que enlaza una propiedad. Veamos como queda el código:

DefaultModelBinderEx
  1. public class DefaultModelBinderEx : DefaultModelBinder
  2. {
  3.     protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext,
  4.         PropertyDescriptor propertyDescriptor)
  5.     {
  6.  
  7.         var cmbattr = propertyDescriptor.Attributes.OfType<CustomModelBinderAttribute>().FirstOrDefault();
  8.         IModelBinder binder;
  9.         if (cmbattr != null && (binder = cmbattr.GetBinder()) != null)
  10.         {
  11.             var subPropertyName = DefaultModelBinder.CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
  12.             var obj = propertyDescriptor.GetValue(bindingContext.Model);
  13.             var modelMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
  14.             modelMetadata.Model = obj;
  15.             var bindingContext1 = new ModelBindingContext()
  16.             {
  17.                 ModelMetadata = modelMetadata,
  18.                 ModelName = subPropertyName,
  19.                 ModelState = bindingContext.ModelState,
  20.                 ValueProvider = bindingContext.ValueProvider
  21.             };
  22.             var propertyValue = this.GetPropertyValue(controllerContext, bindingContext1, propertyDescriptor, binder);
  23.             SetProperty(controllerContext, bindingContext, propertyDescriptor, propertyValue);
  24.             return;
  25.         }
  26.  
  27.         base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
  28.     }
  29. }

Vale… el código luce un “pelín” complicado, pero básicamente hace lo siguiente:

  1. Mira si la propiedad a enlazar está decorada con algún atributo que sea [ModelBinder] o derivado (en este caso será precisamente el [PropertyModelBinder]).
  2. Si dicha propiedad está enlazada obtiene el binder (mediante el método GetBinder() del atributo) y:
    1. Crea un “subcontexto” de binding, que contenga tan solo esta propiedad.
    2. Obtiene el valor a bindea, mediante la llamada a GetPropertyValue, un método definido en el propio DefaultModelBinder, pasándole el subcontexto de binding y el binder a usar.
    3. Llama a SetProperty (otro método de DefaultModelBinder) para establecer el valor de la propiedad
  3. En caso de que la propiedad NO esté decorada con ningún atributo [ModelBinder] o derivado, llama a base.BindProperty para procesarla de forma normal.

Nota: Este es el MÍNIMO código para que funcione, pero ¡ojo! no estamos tratando todas las casuísticas posibles 😉

Por supuesto para que funcione debemos decirle a ASP.NET MVC que el nuevo ModelBinder por defecto es nuestro DefaultModelBinderEx:

  1. ModelBinders.Binders.DefaultBinder = new DefaultModelBinderEx();

Y con esto ya tendríamos habilitados nuestros ModelBinders por propiedad!

Una nota final…

¿Y como tratar todas las casuísticas posibles que antes he dicho que nuestro método BindProperty no trataba y tener así un código “más completo”? Bien, pues la forma más sencilla y rápida es coger el código fuente de ASP.NET MVC y COPIAR TODO el método BindProperty del DefaultModelBinder en el DefaultModelBinderEx. Y luego una vez lo habéis echo localizad la línea:

  1. IModelBinder binder = this.Binders.GetBinder(propertyDescriptor.PropertyType);

Y modificadla por:

  1. var cmbattr = propertyDescriptor.Attributes.
  2.     OfType<CustomModelBinderAttribute>().
  3.     FirstOrDefault();
  4. IModelBinder binder = cmbattr != null
  5.     ? cmbattr.GetBinder()
  6.     : this.Binders.GetBinder(propertyDescriptor.PropertyType);

Y con esto ya tendréis el soporte completo. La verdad es una pena que el DefaultModelBinder no ofrezca un método virtual GetBinder(PropertyDescriptor) ya que si lo tuviese tan solo redefiniendo este método hubiese sido suficiente… Pero en fin… ¡esto es lo que hay!

Espero que os haya resultado interesante!

Modelos de vista dinámicos en asp.net mvc

Muy buenas!

Imagina que tienes la siguiente entidad de un modelo de datos de Entity Framework:

   1: public class Product

   2: {

   3:     public int Id {get; set;}

   4:     [Required]

   5:     public string Name { get; set; }

   6:     public string ShortDescription { get; set; }

   7:     public string LongDescription { get; set; }

   8:     public string Remmarks { get; set; }

   9:     public string LegalNotices { get; set; }

  10:     public string TermsOfUse { get; set; }

  11:     [Column(TypeName="Image")]

  12:     public byte[] Icon { get; set; }

  13:     [Column(TypeName = "Image")]

  14:     public byte[] Screenshot { get; set; }

  15:     [Column(TypeName = "Image")]

  16:     public byte[] ScreenshotHD { get; set; }

  17:     public decimal Price { get; set; }

  18: }

Y que tienes un controlador que recoge los productos que tengas en la BBDD y los pasa a una vista:

   1: public ActionResult Index()

   2: {

   3:     IEnumerable<Product> products = null;

   4:     using (var ctx = new MyContext())

   5:     {

   6:         products = ctx.Products.ToList();

   7:     }

   8:  

   9:     return View(products);

  10: }

Donde la vista se limita a mostrar usar tan solo un par de propiedades de la clase Product:

   1: <h2>Produtcs list</h2>

   2:  

   3: <ul>

   4:     @foreach (var item in Model)

   5:     {

   6:         <li>@item.Name - @Html.ActionLink("View", "Details", new {id=@item.Id})</li>

   7:     }

   8: </ul>

Probablemente habrás visto código muy parecido en muchos blogs o ejemplos. Dicho código funciona correctamente, pero… ¿cual es el problema?

Pues bien, en el caso de entidades grandes, como puede ser esta Product (fíjate que hay 3 campos de imágen, y un par de campos de texto que podrían ser muy largos), el problema es que la consulta que está generando Entity Framework es:

SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[ShortDescription] AS [ShortDescription], [Extent1].[LongDescription] AS [LongDescription], [Extent1].[Remmarks] AS [Remmarks], [Extent1].[LegalNotices] AS [LegalNotices], [Extent1].[TermsOfUse] AS [TermsOfUse], [Extent1].[Icon] AS [Icon], [Extent1].[Screenshot] AS [Screenshot], [Extent1].[ScreenshotHD] AS [ScreenshotHD], [Extent1].[Price] AS [Price]FROM [dbo].[Products] AS [Extent1]

Es decir, estamos trasladando todos los campos desde la base de datos hacia el servidor web para tan solo usar un par de campos en la vissta (Name e Id).

Esto, en el caso de que seleccionemos muchas entidades grandes puede ser un problema. Por supuesto, la solución es muy sencilla, decirle a Entity Framework que tan solo seleccione los campos que deseemos:

   1: products = ctx.Products.Select(p => new { p.Id, p.Name}).ToList();

Con esto la query generada contiene tan solo los campos deseados:

SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name]FROM [dbo].[Products] AS [Extent1]

El tema está ahora en como pasamos esta información a la vista. Anteriormente la vista tenia la declaración @model IEnumerable<Product> pero ahora esto ya no nos sirve y ASP.NET MVC se quejará (con razón) de que el tipo del modelo de la vista no es el que le pasamos, y nos dará un error.

Quitar la directiva @model de la vista no funciona y recibirás un error parecido al siguiente ‘object’ does not contain a definition for ‘Name’. Si quitamos la directiva @model, el modelo de la vista pasa a ser de tipo Object lo que no nos sirve porque ni Name ni Id están definidas en Object.

Una solución es generar un ViewModel, es decir una clase que contenga los datos que la vista requiere:

   1: public class ProductListViewModel

   2: {

   3:     public int Id { get; set; }

   4:     public string Name { get; set; }

   5: }

Luego en EF debemos crear una colección de este objeto, en lugar del objeto anónimo:

   1: var products = ctx.Products.Select(p => new ProductListViewModel 

   2: { 

   3:     Id = p.Id, 

   4:     Name = p.Name 

   5: }).ToList();

Ahora colocando la directiva @model IEnumerable<ProductListViewModel> en nuestra vista todo funciona correctamente.

El problema ahora viene si tenemos muchas vistas que listen distintos campos Product (p. ej. una con Name e Id, otra con Name, Id, Icon y ShortDescription, otra con Name, LongDescription y Remmarks, etc…). Si sigues esta técnica te vas a encontrar con muchos view models distintos, lo cual puede ser tedioso de mantener.

ASP.NET MVC tiene el concepto de tipo de modelo dinámico. Para ello en la vista puedes poner @model dynamic.

Pero… no es oro todo lo que reluce. Si cambias la directiva a @model dynamic y vuelves al código donde usábamos un objeto anónimo… Obtendrás de nuevo el error ‘object’ does not contain a definition for ‘Name’. Es decir, a pesar de que declaras el tipo de modelo como dinámico en la vista, parece que para ASP.NET MVC el modelo sigue siendo un object.

La razón es que cuando usas @model dynamic el objeto que pasas debe estar preparado para interaccionar con el DLR y los objetos anónimos no lo están. La solución pasa por usar ExpandoObject que si que lo está. Entonces el código del controlador pasa a:

   1: var products = ctx.Products.Select(p => new

   2: {

   3:     p.Id,

   4:     p.Name

   5: }).ToList();

   6:  

   7: List<dynamic> model = new List<dynamic>();

   8: foreach (var item in products)

   9: {

  10:     dynamic data = new ExpandoObject();

  11:     data.Id = item.Id;

  12:     data.Name = item.Name;

  13:     model.Add(data);

  14: }

  15:  

  16: return View(model);

Ahora usando @model dynamic todo funciona correctamente. Por supuesto parece que el remedio es peor que la enfermedad: ¡El código es bastante más pesado y tienes que realizar esta copia de “propiedades” del objeto anónimo al ExpandoObject!

Ahí es donde una característica de ExpandoObject y un método de extensión te pueden ayudar. La característica es que ExpandoObject implementa IDictionary<string,object> y agregar una propiedad a ExpandoObject es equivalente a agregar una entrada en el diccionario. Así podemos tener un par de métodos de extensión sencillos:

   1: static class ExpandoObjectExtensions

   2: {

   3:     public static dynamic CopyFrom(this ExpandoObject source, object data)

   4:     {

   5:         var dict = source as IDictionary<string, object>;

   6:         foreach (var property in data.GetType().GetProperties())

   7:         {

   8:             dict.Add(property.Name, property.GetValue(data, null));

   9:         }

  10:  

  11:         return source;

  12:     }

  13:  

  14:     public static IEnumerable<dynamic> Dynamize<T>(this IEnumerable<T> source)

  15:     {

  16:         foreach (var entry in source)

  17:         {

  18:             var expando = new ExpandoObject();

  19:             yield return expando.CopyFrom(entry);

  20:         }

  21:     }

  22: }

Y ahora nuestro código en el controlador queda simplemente como:

   1: var products = ctx.Products.Select(p => new

   2:                 {

   3:                     p.Id,

   4:                     p.Name

   5:                 }).ToList().Dynamize();

¡Y listos! Usando @model dynamic nuestra vista sigue funcionando y nos olvidamos de ir creando viewmodels y de copiar propiedades arriba y abajo.

Espero que os haya sido interesante! 😉

¡Un saludo!

¡Hello World Katana!

Buenas! En este post vamos a ver como empezar a trabajar con Katana. En un post anterior hablé un poco de Katana y mis fantasías (más o menos húmedas) de lo que podría ser un un futuro.

Antes que nada hagamos un repaso rápido:

  1. OWIN: Open Web Interface for .NET. Especificación que define un estándard para comunicar servidores web y aplicaciones web (en tecnología .NET).
  2. Katana: Implementación de Microsoft de la especificación OWIN.

¿Cuál es la ventaja principal de hacer que una aplicación web sea compatible con OWIN? Pues simplemente que desacoplas esta aplicación web del servidor usado. Cualquier servidor (que sea compatible con OWIN) podrá hospedar tu aplicación. Esto abre la puerta a tener aplicaciones web self-hosted.

Empezando con Owin y Visual Studio 2012

En este primer post vamos a realizar la aplicación más posible sencilla (un hello world, originalidad a tope).

Para empezar abre VS2012 (o VS2013 si lo tienes, para este post da igual) y crea una aplicación de consola. Luego añade con NuGet los siguientes paquetes:

  1. Microsoft.Owin.Hosting
  2. Microsoft.Owin.Host.HttpListener
  3. Microsoft.Owin.Diagnostics
  4. Owin.Extensions

Actualmente están en prerelase así que incluye el flag –IncludePreRelase  cuando lances el comando Install-Package desde la consola de NuGet.

Una vez tengos estos paquetes instalados, ya podemos desarrollar nuestra aplicación. Lo primero que necesitamos es una clase que ponga en marcha nuestra aplicación:

class Program

{

    public static void Main(string[] args)

    {

        var uri = "http://localhost:8080/";

 

        using (WebApp.Start<Startup>(uri))

        {

            Console.WriteLine("Started");

            Console.ReadKey();

            Console.WriteLine("Stopping");

        }

    }

}

Usamos la clase WebApp del paquete Microsoft.Owin.Hosting para poner en marcha nuestra aplicación. El parámetro genérico (Startup) es el nombre de una clase que será la que configurará nuestra aplicación.

Veamos el código:

public class Startup

{

    public void Configuration(IAppBuilder app)

    {

        app.UseHandlerAsync((req, res) =>

        {

            res.ContentType = "text/plain";

            return res.WriteAsync("Hello Katana. You reached " + req.Uri);

        });

 

    }

}

El método Configuration se invoca automáticamente y se recibe un parámetro IAppBuilder. Dicha interfaz estaba definida en la specificación de OWIN (http://owin.org/spec/owin-0.12.0.html#_2.10._IAppBuilder) pero desapareció en la versión final.

Katana usa esta interfaz para permitir a la aplicación web configurar el pipeline de procesamiento de peticiones. De momento nuestra aplicación es muy simple: Por cada petición, construirá una respuesta con el texto “Hello Katana. You reached “ seguido de la URL navegada.

Si ejecutamos el proyecto, y abrimos un navegador y nos vamos a localhost:8080, vemos que nuestra aplicación ya está en marcha:

image

Fíjate que nuestra aplicación es un ejecutable. No hay servidor web, ni cassini, ni IIS Express, ni IIS, ni nada 🙂

Agregando un módulo

OWIN se define de forma totalmente modular. Por un lado tenemos un Host (en este caso es nuestro ejecutable a través del objeto WebApp del paquete Microsoft.Owin.Host), varios módulos y finalmente la aplicación en si.

Los módulos implementan un “delegado” que se conoce como AppFunc (aunque no hay ningún delegado real con este nombre). AppFunc es realmente Func<IDictionary<string, object>, Task>, es decir recibir un IDictionary<string, object> y devolver un Task.

La idea es que un módulo recibe un diccionario (claves cadenas, valores objects) que es el entorno del servidor y devuelve una Task que es el código que este módulo debe ejecutar.

Los módulos están encadenados y cada módulo debe llamar al siguiente. El aspecto genérico de un módulo queda así:

using AppFunc = Func<IDictionary<string, object>, Task>;

public class OwinConsoleLog

{

    private readonly AppFunc _next;

    public OwinConsoleLog(AppFunc next)

    {

        _next = next;

    }

    public Task Invoke(IDictionary<string, object> environment)

    {

        Console.WriteLine("Path requested: {0}", environment["owin.RequestPath"]);

        return _next(environment);

    }

}

El módulo define el método Invoke y recibe como parámetro el diccionario que contiene el entorno del servidor. Luego llama al siguiente módulo y le pasa el entorno. Fíjate que la clase OwinConsoleLog no implementa ninguna interfaz ni nada, pero debe tener un método llamado Invoke que sea conforme al “delegado” AppFunc (es decir que devuelva un Task y reciba un diccionario).

Para añadir el módulo simplemente llamamos al método Use de IAppBuilder pasándole el Type de nuestro módulo:

public void Configuration(IAppBuilder app)

{

    app.Use(typeof (OwinConsoleLog));

    app.UseHandlerAsync((req, res) =>

    {

        res.ContentType = "text/plain";

        return res.WriteAsync("Hello Katana. You reached " + req.Uri);

    });

}

Si ahora ejecutas el proyecto y navegas a localhost:8080 verás como se imprimen las distintas peticiones recibidas:

image

¡Listos! Hemos creado nuestra primera aplicación web, compatible con OWIN y auto hospedada 🙂

En sucesivos posts iremos desgranando más cosillas sobre OWIN y Katana…

Saludos!