Identidad de ASP.NET confusión de DbContext

Una aplicación MVC 5 predeterminada viene con este código en IdentityModels.cs: este fragmento de código es para todas las operaciones de Identidad ASP.NET para las plantillas predeterminadas:

public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } } 

Si andamio un nuevo controlador usando vistas con Entity Framework y creo un “Nuevo contexto de datos …” en el cuadro de diálogo, obtengo esto generado para mí:

 using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; namespace WebApplication1.Models { public class AllTheOtherStuffDbContext : DbContext { // You can add custom code to this file. Changes will not be overwritten. // // If you want Entity Framework to drop and regenerate your database // automatically whenever you change your model schema, please use data migrations. // For more information refer to the documentation: // http://msdn.microsoft.com/en-us/data/jj591621.aspx public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext") { } public System.Data.Entity.DbSet Movies { get; set; } } } 

Si andamio otro controlador + vista usando EF, digamos por ejemplo para un modelo Animal, esta nueva línea se generaría automáticamente justo debajo de public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } – como esto:

 using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Web; namespace WebApplication1.Models { public class AllTheOtherStuffDbContext : DbContext { // You can add custom code to this file. Changes will not be overwritten. // // If you want Entity Framework to drop and regenerate your database // automatically whenever you change your model schema, please use data migrations. // For more information refer to the documentation: // http://msdn.microsoft.com/en-us/data/jj591621.aspx public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext") { } public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Animals { get; set; } } } 

ApplicationDbContext (para todo el material de identidad de ASP.NET) hereda de IdentityDbContext que a su vez hereda de DbContext . AllOtherStuffDbContext (para mis cosas) hereda de DbContext .

Entonces mi pregunta es:

¿Cuál de estos dos ( ApplicationDbContext y AllOtherStuffDbContext ) debería usar para todos mis otros modelos? ¿O debería usar el ApplicationDbContext autogenerado por defecto, ya que no debería ser un problema usarlo ya que deriva de la clase base DbContext , o habrá algún exceso? Deberías usar solo un objeto DbContext en tu aplicación para todos tus modelos (lo he leído en alguna parte), así que ni siquiera debería considerar usar tanto ApplicationDbContext como AllOtherStuffDbContext en una sola aplicación. ¿O cuál es la mejor práctica en MVC 5 con ASP.NET Identity?

Utilizaría una única clase Context heredando de IdentityDbContext. De esta forma, puede hacer que el contexto esté al tanto de cualquier relación entre sus clases y el Usuario de Identidad y Roles de IdentityDbContext. Hay muy poca sobrecarga en el IdentityDbContext, básicamente es un DbContext regular con dos DbSets. Uno para los usuarios y otro para los roles.

Hay mucha confusión sobre IdentityDbContext , una búsqueda rápida en Stackoverflow y encontrará estas preguntas:
” ¿Por qué Asp.Net Identity IdentityDbContext es una Black-Box?
¿Cómo puedo cambiar los nombres de las tablas cuando uso Visual Studio 2013 AspNet Identity?
Fusionar MyDbContext con IdentityDbContext ”

Para responder a todas estas preguntas solo necesitamos entender que IdentityDbContext es solo una clase heredada de DbContext.
Echemos un vistazo a la fuente IdentityDbContext :

 ///  /// Base class for the Entity Framework database context used for identity. ///  /// The type of user objects. /// The type of role objects. /// The type of the primary key for users and roles. /// The type of the user claim object. /// The type of the user role object. /// The type of the user login object. /// The type of the role claim object. /// The type of the user token object. public abstract class IdentityDbContext : DbContext where TUser : IdentityUser where TRole : IdentityRole where TKey : IEquatable where TUserClaim : IdentityUserClaim where TUserRole : IdentityUserRole where TUserLogin : IdentityUserLogin where TRoleClaim : IdentityRoleClaim where TUserToken : IdentityUserToken { ///  /// Initializes a new instance of . ///  /// The options to be used by a . public IdentityDbContext(DbContextOptions options) : base(options) { } ///  /// Initializes a new instance of the  class. ///  protected IdentityDbContext() { } ///  /// Gets or sets the  of Users. ///  public DbSet Users { get; set; } ///  /// Gets or sets the  of User claims. ///  public DbSet UserClaims { get; set; } ///  /// Gets or sets the  of User logins. ///  public DbSet UserLogins { get; set; } ///  /// Gets or sets the  of User roles. ///  public DbSet UserRoles { get; set; } ///  /// Gets or sets the  of User tokens. ///  public DbSet UserTokens { get; set; } ///  /// Gets or sets the  of roles. ///  public DbSet Roles { get; set; } ///  /// Gets or sets the  of role claims. ///  public DbSet RoleClaims { get; set; } ///  /// Configures the schema needed for the identity framework. ///  ///  /// The builder being used to construct the model for this context. ///  protected override void OnModelCreating(ModelBuilder builder) { builder.Entity(b => { b.HasKey(u => u.Id); b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique(); b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex"); b.ToTable("AspNetUsers"); b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken(); b.Property(u => u.UserName).HasMaxLength(256); b.Property(u => u.NormalizedUserName).HasMaxLength(256); b.Property(u => u.Email).HasMaxLength(256); b.Property(u => u.NormalizedEmail).HasMaxLength(256); b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired(); b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired(); b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); }); builder.Entity(b => { b.HasKey(r => r.Id); b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex"); b.ToTable("AspNetRoles"); b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken(); b.Property(u => u.Name).HasMaxLength(256); b.Property(u => u.NormalizedName).HasMaxLength(256); b.HasMany(r => r.Users).WithOne().HasForeignKey(ur => ur.RoleId).IsRequired(); b.HasMany(r => r.Claims).WithOne().HasForeignKey(rc => rc.RoleId).IsRequired(); }); builder.Entity(b => { b.HasKey(uc => uc.Id); b.ToTable("AspNetUserClaims"); }); builder.Entity(b => { b.HasKey(rc => rc.Id); b.ToTable("AspNetRoleClaims"); }); builder.Entity(b => { b.HasKey(r => new { r.UserId, r.RoleId }); b.ToTable("AspNetUserRoles"); }); builder.Entity(b => { b.HasKey(l => new { l.LoginProvider, l.ProviderKey }); b.ToTable("AspNetUserLogins"); }); builder.Entity(b => { b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name }); b.ToTable("AspNetUserTokens"); }); } } 

Según el código fuente, si queremos fusionar IdentityDbContext con nuestro DbContext tenemos dos opciones:

Primera opción:
Cree un DbContext que herede de IdentityDbContext y tenga acceso a las clases.

  public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } static ApplicationDbContext() { Database.SetInitializer(new ApplicationDbInitializer()); } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } // Add additional items here as needed } 

Notas adicionales:

1) También podemos cambiar los nombres de la tabla predeterminada de identidad de asp.net con la siguiente solución:

  public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(): base("DefaultConnection") { } protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().ToTable("user"); modelBuilder.Entity().ToTable("user"); modelBuilder.Entity().ToTable("role"); modelBuilder.Entity().ToTable("userrole"); modelBuilder.Entity().ToTable("userclaim"); modelBuilder.Entity().ToTable("userlogin"); } } 

2) Además, podemos extender cada clase y agregar cualquier propiedad a clases como ‘IdentityUser’, ‘IdentityRole’, …

  public class ApplicationRole : IdentityRole { public ApplicationRole() { this.Id = Guid.NewGuid().ToString(); } public ApplicationRole(string name) : this() { this.Name = name; } // Add any custom Role properties/code here } // Must be expressed in terms of our custom types: public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } static ApplicationDbContext() { Database.SetInitializer(new ApplicationDbInitializer()); } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } // Add additional items here as needed } 

Para ahorrar tiempo, podemos utilizar la plantilla de proyecto extensible AspNet Identity 2.0 para extender todas las clases.

Segunda opción: (No recomendado)
En realidad, no tenemos que heredar de IdentityDbContext si escribimos todo el código nosotros mismos.
Básicamente, podemos heredar de DbContext e implementar nuestra versión personalizada de “OnModelCreating (generador de ModelBuilder)” del código fuente de IdentityDbContext

Si profundiza en las abstracciones de IdentityDbContext, verá que se parece a su DbContext derivado. La ruta más fácil es la respuesta de Olav, pero si desea tener más control sobre lo que se está creando y una menor dependencia de los paquetes de Identidad, eche un vistazo a mi pregunta y respuesta aquí . Hay un ejemplo de código si sigue el enlace, pero en resumen solo agrega los DbSets necesarios a su propia subclase DbContext.

Esta es una entrada tardía para la gente, pero debajo está mi implementación. También notará que apagué la capacidad de cambiar el tipo predeterminado de KEYs: los detalles sobre los cuales se pueden encontrar en los siguientes artículos:

  • Extender modelos de identidad y usar claves de enteros en lugar de cadenas
  • Cambiar clave principal para usuarios en identidad ASP.NET

NOTAS:
Debe tenerse en cuenta que no puede usar Guid's para sus claves. Esto se debe a que, bajo el capó, son un Struct y, como tales, no tienen unboxing que permita su conversión desde un parámetro genérico .

LAS CLASES MIRAN COMO:

 public class ApplicationDbContext : IdentityDbContext { #region  public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess) { } #endregion #region  //public DbSet Case { get; set; } #endregion #region  #region protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); //modelBuilder.Configurations.Add(new ResourceConfiguration()); //modelBuilder.Configurations.Add(new OperationsToRolesConfiguration()); } #endregion #region public static ApplicationDbContext Create() { return new ApplicationDbContext(); } #endregion #endregion } public class ApplicationUser : IdentityUser { #region  public ApplicationUser() { Init(); } #endregion #region  [Required] [StringLength(250)] public string FirstName { get; set; } [Required] [StringLength(250)] public string LastName { get; set; } #endregion #region  #region private private void Init() { Id = Guid.Empty.ToString(); } #endregion #region public public async Task GenerateUserIdentityAsync(UserManager manager) { // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); // Add custom user claims here return userIdentity; } #endregion #endregion } public class CustomUserStore : UserStore { #region  public CustomUserStore(ApplicationDbContext context) : base(context) { } #endregion } public class CustomUserRole : IdentityUserRole { } public class CustomUserLogin : IdentityUserLogin { } public class CustomUserClaim : IdentityUserClaim { } public class CustomRoleStore : RoleStore { #region  public CustomRoleStore(ApplicationDbContext context) : base(context) { } #endregion } public class CustomRole : IdentityRole { #region  public CustomRole() { } public CustomRole(string name) { Name = name; } #endregion }