Pourquoi Asp.Net Identity IdentityDbContext une boîte noire?

Il y a beaucoup de confusion autour D'IdentityDbContext.

Si nous créons deux contextes de base de données dans notre application, L'un pour Identity et l'autre pour nos données métier personnalisées, le contexte de base de données D'identité hérite D'IdentityDbContext tandis que nos données métier personnalisées héritent de DbContext.

Ajoutons donc ce qui suit à un contrôleur:

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

Et ce qui suit à une méthode D'Index dans le contrôleur:

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

Vous remarquerez également que les tables les bases de données Indentity ne sont pas disponibles. pourquoi?

Actuellement, à partir de 2.0.0-beta1, il existe des éléments utilisateurs et rôles, mais je m'attendais à ce que les tables réelles soient disponibles. Et pourquoi pas? et si je voulais me rendre à AspNetUserRoles

Semble sûr que beaucoup de confusion et de problèmes avec Asp.Net L'identité disparaîtrait si elle était traitée comme n'importe quel contexte de base de données dans Entity Framework.

30
demandé sur Sean Newcome 2014-03-01 00:55:58

4 réponses

Les propriétés ApplicationDbContext Users et Roles sont mappées aux tables AspNetUsers et AspNetRoles, ainsi qu'au reste des entités (Claims, Logins, UserRoles) sont mappés automatiquement via les propriétés de navigation. Pour autant que je sache, le préfixe des noms de table avec "AspNet" sont les seuls mappages personnalisés dans ApplicationDbContext, tout le reste n'est que des conventions de code Entity Framework First.

Si vous avez besoin d'un accès direct aux tables via le ApplicationDbContext, vous pouvez le faire comme ceci...

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();
}

Vous pouvez accéder à rôles, revendications et connexions via les propriétés de navigation sur l'entité IdentityUser Users DbSet). Si vous voulez les interroger directement, ajoutez-les explicitement en tant que DbSet s sur le contexte...

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; }

}

Et interrogez-les comme ceci...

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

ASP.NET Identity 2.0 expose Users et Roles IQueryables sur les classes de gestionnaire pour plus de commodité, mais il ne fournit aucune fonctionnalité supplémentaire par rapport à ce qui était disponible à partir de DbContext.

29
répondu Anthony Chu 2014-02-28 21:41:28

Il y a un malentendu fondamental ici sur le fonctionnement de DbContext. Les noms de propriétés de vos DbSet dans votre contexte ne correspondent pas aux noms de table. Le cas échéant, le nom de la table est basé sur le nom de classe de l'entité réelle, mais même cela peut être remplacé. Un exemple parfait est bien sûr votre classe utilisateur, qui est par défaut ApplicationUser, mais résidera dans une table appelée AspNetUsers.

Toutes les propriétés DbSet dans votre contexte déterminent l'API que vous utilisez pour accéder aux données via Entity Framework. IdentityDbContext implémente DbSet Nom des propriétésUsers, Roles, etc. C'est ainsi que vous accédez à ces données, Pas via le nom de la table (c'est-à-dire context.Users).

De plus, si vous n'êtes pas satisfait d'avoir deux contextes, vous n'avez pas à les garder comme deux. Faites simplement hériter votre contexte principal de IdentityDbContext<ApplicationUser> au lieu de DbContext et tuez la version échafaudée.

8
répondu Chris Pratt 2014-02-28 21:56:24

Même si les tables d'identité de la base de données sont nommées avec le préfixe aspnet, Vous pouvez toujours les changer . Mais pas toujours le nom de la table dans la base de données ne sera pas celui que vous verrez lors de l'accès à partir de DbContext. Vous devrez travailler avec des noms générés par le framework. Mais cela peut être changé aussi. Voir Modèle de données D'identité avec L'Entity Framework Fluent .

1
répondu Nipuna 2017-05-23 12:26:01

Il y a certainement beaucoup de confusion autour IdentityDbContext, une recherche rapide autour de SO et vous trouverez beaucoup de questions sur ce sujet.
ASP.NET identité confusion DbContext
Comment puis-je modifier les noms de table lors de L'utilisation de Visual Studio 2013 AspNet Identity?
Fusion MyDbContext avec IdentityDbContext

La réponse à toutes ces questions, nous devons d'abord comprendre comment fonctionne IdentityDbContext. Pour clarifier les choses, nous devrions prendre en compte considérez que IdentityDbContext est juste une classe héritée de DbContext et non une boîte noire!
Jetons un coup d'oeil à IdentityDbContext source:

/// <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");
        });
    }
}

Basé sur le code source, tout ce que vous avez à faire est de créer un DbContext qui hérite de IdentityDbContext et a accès aux classes.

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 vous souhaitez étendre les classes ont un coup d'oeil à AspNet Identité 2.0 Extensible Projet Modèle

1
répondu Arvand 2017-07-31 20:21:30