c# - framework - ¿Por qué Asp.Net Identity IdentityDbContext es Black-Box?
asp.net membership vs identity (4)
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<ApplicationUser>
lugar de DbContext
y DbContext
la versión scaffolded.
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.
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 .
Las propiedades de Users
y Roles
ApplicationDbContext
se asignan a las tablas AspNetUsers
y AspNetRoles
, y el resto 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<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<IdentityUserRole> UserRoles { get; set; }
public DbSet<IdentityUserClaim> Claims { get; set; }
public DbSet<IdentityUserLogin> 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.
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 :
/// <summary>
/// Base class for the Entity Framework database context used for identity.
/// </summary>
/// <typeparam name="TUser">The type of user objects.</typeparam>
/// <typeparam name="TRole">The type of role objects.</typeparam>
/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>
/// <typeparam name="TUserClaim">The type of the user claim object.</typeparam>
/// <typeparam name="TUserRole">The type of the user role object.</typeparam>
/// <typeparam name="TUserLogin">The type of the user login object.</typeparam>
/// <typeparam name="TRoleClaim">The type of the role claim object.</typeparam>
/// <typeparam name="TUserToken">The type of the user token object.</typeparam>
public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : DbContext
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserRole : IdentityUserRole<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TRoleClaim : IdentityRoleClaim<TKey>
where TUserToken : IdentityUserToken<TKey>
{
/// <summary>
/// Initializes a new instance of <see cref="IdentityDbContext"/>.
/// </summary>
/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
public IdentityDbContext(DbContextOptions options) : base(options)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="IdentityDbContext" /> class.
/// </summary>
protected IdentityDbContext()
{ }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of Users.
/// </summary>
public DbSet<TUser> Users { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User claims.
/// </summary>
public DbSet<TUserClaim> UserClaims { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User logins.
/// </summary>
public DbSet<TUserLogin> UserLogins { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User roles.
/// </summary>
public DbSet<TUserRole> UserRoles { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User tokens.
/// </summary>
public DbSet<TUserToken> UserTokens { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of roles.
/// </summary>
public DbSet<TRole> Roles { get; set; }
/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of role claims.
/// </summary>
public DbSet<TRoleClaim> RoleClaims { get; set; }
/// <summary>
/// Configures the schema needed for the identity framework.
/// </summary>
/// <param name="builder">
/// The builder being used to construct the model for this context.
/// </param>
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<TUser>(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<TRole>(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<TUserClaim>(b =>
{
b.HasKey(uc => uc.Id);
b.ToTable("AspNetUserClaims");
});
builder.Entity<TRoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("AspNetRoleClaims");
});
builder.Entity<TUserRole>(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("AspNetUserRoles");
});
builder.Entity<TUserLogin>(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
b.ToTable("AspNetUserLogins");
});
builder.Entity<TUserToken>(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<ApplicationDbContext>(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