web-dev-qa-db-ja.com

Asp.Net Identity IdentityDbContextがブラックボックスなのはなぜですか?

IdentityDbContextに関しては多くの混乱があります。

アプリケーションに2つのデータベースコンテキスト(Identity用とカスタムビジネスデータ用)を作成する場合、Identity DatabaseコンテキストはIdentityDbContextから継承し、カスタムビジネスデータはDbContextから継承します。

それでは、コントローラに次を追加しましょう。

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

また、コントローラーのIndexメソッドには次のようになります。

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

また、Indentity Databaseのテーブルが利用できないことにも気づくでしょう。 これはなぜですか?

現在、2.0.0-beta1の時点で、UsersとRolesの項目がありますが、実際のテーブルが利用可能になると予想していました。 そしてなぜそうではないのですか? AspNetUserRolesにアクセスしたい場合

確かに、Asp.Net Identityの多くの混乱と問題は、Entity Frameworkのデータベースコンテキストのように扱われれば消えるでしょう。

34
Sean Newcome

ApplicationDbContextUsersおよびRolesプロパティは、AspNetUsersおよびAspNetRolesテーブル、および残りのエンティティ(ClaimsLoginsUserRoles)は、ナビゲーションプロパティを介して自動的にマッピングされます。私の知る限り、テーブル名の「AspNet」というプレフィックスは、ApplicationDbContextの唯一のカスタムマッピングであり、それ以外はすべてEntity Framework Code Firstの規則です。

ApplicationDbContextを介してテーブルに直接アクセスする必要がある場合、次のようにできます。

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

IdentityUserエンティティのナビゲーションプロパティを介して(UsersDbSetから)ユーザーのロール、クレーム、およびログインにアクセスできます。それらを直接クエリしたい場合は、コンテキストにDbSetsとして明示的に追加してください...

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

}

そして、次のようにそれらをクエリします...

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

ASP.NET Identity 2.0は、便宜上、ManagerクラスでUsersおよびRolesIQueryablesを公開しますが、DbContextから利用できる機能に対する追加機能は提供しません。

31
Anthony Chu

DbContextがどのように機能するかについて、根本的な誤解があります。コンテキストのDbSetsのプロパティ名しないはテーブル名に対応します。どちらかといえば、テーブル名は実際のエンティティのクラス名に基づいていますが、それでも上書きすることができます。完璧な例はもちろん、デフォルトではApplicationUserですが、AspNetUsersと呼ばれるテーブルに常駐するユーザークラスです。

コンテキストが決定するすべてのDbSetプロパティは、Entity Frameworkを介してデータにアクセスするために使用するAPIです。 IdentityDbContextDbSetプロパティ名UsersRolesなどを実装します。そのため、そのデータにアクセスする方法はnotテーブル名(つまりcontext.Users)。

さらに、2つのコンテキストがあることに不満がある場合は、それらを2つのままにする必要はありません。メインコンテキストをDbContextではなくIdentityDbContext<ApplicationUser>から継承し、scaffoldedバージョンを強制終了します。

9
Chris Pratt

IdentityDbContextの周りには確かに多くの混乱があり、SOの周りをすばやく検索すると、このトピックに関する多くの質問が見つかります。
ASP.NET Identity DbContext confusion
Visual Studio 2013 AspNet Identityを使用しているときにテーブル名を変更するにはどうすればよいですか?
MyDbContextをIdentityDbContextとマージ

これらすべての質問に対する答えは、まずIdentityDbContextがどのように機能するかを理解する必要があります。物事を明確にするために、IdentityDbContextはDbContextから継承されたクラスであり、ブラックボックスではないことを考慮する必要があります。
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");
        });
    }
}

ソースコードに基づいて行う必要があるのは、IdentityDbContextを継承し、クラスにアクセスできるDbContextを作成することだけです。

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
}

クラスをさらに拡張する場合は、 AspNet Identity 2.0 Extensible Project Template を参照してください。

1
Arvand

データベースのIDテーブルの名前はaspnetプレフィックスで取得されますが、常に 変更する にすることができます。ただし、データベース内のテーブル名がDbContextからアクセスしたときに表示される名前とは限りません。フレームワークによって生成される名前を使用する必要があります。しかし、これも変更できます。 Entity Framework Fluentを使用したIDデータモデル を参照してください。

1
Nipuna