¿Por qué Asp.Net Identity IdentityDbContext es Black-Box?

Hay mucha confusión alrededor de IdentityDbContext.

Si creamos dos Contextos de base de datos en nuestra aplicación, uno para Identidad y uno para nuestros datos comerciales personalizados, el contexto de la base de datos de identidad hereda de IdentityDbContext, mientras que nuestros datos comerciales personalizados heredan de DbContext.

Así que agreguemos lo siguiente a un controlador:

private MyDbContext db = new MyDbContext(); private ApplicationDbContext identityDb = new ApplicationDbContext(); 

Y lo siguiente a un método de índice en el controlador:

 var thingsInMyBusinessDb = db.Things.ToList(); var usersInIndentityDb = identityDb.AspNetUsers.ToList(); // THIS WILL HAVE AN ERROR var roles = identityDb.AspNetRoles.ToList(); // ERROR 

También notará que las tablas en la base de datos de Indentity no están disponibles. ¿Por qué es esto?

Actualmente, a partir de 2.0.0-beta1, hay elementos de Usuarios y Roles, pero esperaba que las tablas reales estuvieran disponibles. ¿Y por qué no? ¿Qué sucede si quiero llegar a AspNetUserRoles?

Seguro parece que mucha confusión y problemas con Asp.Net Identity desaparecerían si se trataran como cualquier contexto de base de datos en Entity Framework.

Las propiedades de Users y Roles ApplicationDbContext se asignan a las tablas AspNetUsers y AspNetRoles , y el rest de las entidades ( UserRoles , Logins , UserRoles ) se asignan automáticamente a través de las propiedades de navegación. Hasta donde yo sé, el prefijo de los nombres de tabla con “AspNet” son las únicas asignaciones personalizadas en ApplicationDbContext , todo lo demás es solo convenciones de Entity Framework Code First.

Si necesita acceso directo a las tablas a través de ApplicationDbContext , puede hacerlo así …

 using (var context = new ApplicationDbContext()) { var users = context.Users.Include(u => u.Claims) .Include(u => u.Logins) .Include(u => u.Roles) .ToList(); var roles = context.Roles.ToList(); } 

Puede acceder a las funciones, los reclamos y los inicios de sesión de un usuario a través de las propiedades de navegación en la entidad IdentityUser (del DbSet ). Si desea consultarlos directamente, agréguelos de forma explícita como DbSet s en el contexto …

 public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } public DbSet UserRoles { get; set; } public DbSet Claims { get; set; } public DbSet Logins { get; set; } } 

Y consultarlos de esta manera …

 var claims = context.Claims.ToList(); var userRoles = context.UserRoles.ToList(); var logins = context.Logins.ToList(); 

ASP.NET Identity 2.0 expone Users y Roles IQueryable s en las clases de Manager para mayor comodidad, pero no proporciona ninguna funcionalidad adicional sobre lo que estaba disponible desde DbContext.

Aquí hay un malentendido fundamental sobre cómo funciona DbContext . Los nombres de las propiedades de sus DbSet en su contexto no corresponden a los nombres de las tablas. En todo caso, el nombre de la tabla se basa en el nombre de la clase de la entidad real, pero incluso eso puede anularse. Un ejemplo perfecto es, por supuesto, su clase de usuario, que es ApplicationUser por defecto, pero residirá en una tabla llamada AspNetUsers .

Todas las propiedades de DbSet en su contexto determinan la API que utiliza para acceder a los datos a través de Entity Framework. IdentityDbContext implementa el nombre de propiedades de DbSet Users , Roles , etc. Así es como se accede a esos datos, no a través del nombre de la tabla (es decir, Users DbSet ).

Además, si no está contento con tener dos contextos, no tiene que mantenerlos como dos. Simplemente haga que su contexto principal herede de IdentityDbContext lugar de DbContext y DbContext la versión scaffolded.

Aunque las tablas de identidad en la base de datos se nombran con el prefijo aspnet , siempre puede cambiarlas . Pero no siempre el nombre de la tabla en la base de datos no será el que verá al acceder desde DbContext . Deberá trabajar con los nombres que genera el marco. Pero esto también se puede cambiar. Ver Modelo de datos de identidad con Entity Framework Fluent .

Sin duda, hay mucha confusión en torno a IdentityDbContext, una búsqueda rápida en torno a SO y encontrará muchas preguntas sobre este tema.
Identidad de ASP.NET confusión de DbContext
¿Cómo puedo cambiar los nombres de las tablas cuando uso Visual Studio 2013 AspNet Identity?
Fusionar MyDbContext con IdentityDbContext

La respuesta a todas estas preguntas es que primero debemos entender cómo funciona IdentityDbContext. Para aclarar las cosas, debemos tener en cuenta que IdentityDbContext es solo una clase heredada de DbContext y no una caja negra.
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"); }); } } 

Basado en el código fuente, todo lo que tiene que hacer es crear 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 } 

Si desea ampliar aún más las clases, eche un vistazo a AspNet Identity 2.0 Extensible Project Template