为什么Asp.Net Identity IdentityDbContext是黑盒子?

时间:2014-02-28 20:55:59

标签: c# asp.net asp.net-mvc entity-framework asp.net-identity

IdentityDbContext周围有很多混乱。

如果我们在应用程序中创建两个数据库上下文,一个用于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数据库中的表格不可用。 为什么会这样?

目前从2.0.0-beta1开始,有一个用户和角色项目,但我希望实际的表格可用。 为什么不呢? 如果我想访问AspNetUserRoles怎么办

如果它被视为实体框架中的任何数据库上下文,那么Asp.Net Identity的许多混淆和问题肯定会消失。

4 个答案:

答案 0 :(得分:30)

ApplicationDbContext的{​​{1}}和Users属性映射到RolesAspNetUsers表以及其他实体({{1 },AspNetRolesClaims)通过导航属性自动映射。据我所知,表名与“AspNet”的前缀是Logins中唯一的自定义映射,其他一切只是实体框架代码优先约定。

如果您需要通过UserRoles直接访问表格,可以这样做......

ApplicationDbContext

您可以通过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)的导航属性访问用户的角色,声明和登录信息。如果您想直接查询它们,请在上下文中将Users显式添加...

DbSet

然后像这样查询......

DbSet

为了方便起见,ASP.NET Identity 2.0在Manager类上公开了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(); Users,但它没有提供任何来自DbContext可用内容的功能。

答案 1 :(得分:9)

这里有一个关于DbContext如何运作的根本误解。您的上下文DbSet的属性名称与表名相对应。如果有的话,表名基于实际实体的类名,但即使这样也可以被覆盖。一个完美的示例当然是您的用户类,默认情况下为ApplicationUser,但它将驻留在名为AspNetUsers的表中。

上下文中的所有DbSet属性都是您用于通过Entity Framework访问数据的API。 IdentityDbContext实现DbSet属性名称UsersRoles等。这就是您通过表名访问该数据的方式,(即context.Users)。

此外,如果你对两个上下文不满意,你不必将它们保持为两个。只需让您的主要上下文继承自IdentityDbContext<ApplicationUser>而不是DbContext,并删除脚手架版本。

答案 2 :(得分:1)

即使数据库中的标识表以aspnet前缀命名,您始终可以change them。但并非总是数据库中的表名不会是您从DbContext访问时看到的表名。您需要使用框架生成的名称。但这也可以改变。请参阅Identity Data Model with the Entity Framework Fluent

答案 3 :(得分:1)

IdentityDbContext肯定存在很多混乱,围绕SO快速搜索,你会发现很多关于这个主题的问题。
ASP.NET Identity DbContext confusion
How can I change the table names when using Visual Studio 2013 AspNet Identity?
Merge MyDbContext with 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");
        });
    }
}

基于源代码,您所要做的就是创建一个DbContext,它继承自IdentityDbContext并可以访问这些类。

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