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!

4 comentarios en “Cambiar el esquema con ASP.NET Identity”

  1. Estimado y como se podría agregar index unique a “UserName” a la tabla AspNetUsers.
    Quiero que dicha tabla tenga una columna que identifica compañia y cada compañia pueda tener su propio usuario

    1. Tienes que modificar la clase de usuarios generada por VisualStudio (ApplicationUser creo que se llama) y luego añadir una migración (Add-Migration) y ejecutarla (Update-Database)

  2. Hola, excelente artículo, solo que he tenido un problema, no me deja cambiar el nombre de la tabla AspNetUsers, sale este error “2BP01: no se puede eliminar restricción «PK_AspNetUsers» en tabla “AspNetUsers” porque otros objetos dependen de él”, agradezco la ayuda.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *