首页 文章

ASP.NET Identity DbContext混淆

提问于
浏览
162

默认的MVC 5 App附带了IdentityModels.cs中的这段代码 - 这段代码用于默认模板的所有ASP.NET Identity操作:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

如果我使用带有Entity Framework的视图构建一个新控制器并在对话框中创建一个“新数据上下文...”,我会为我生成这个:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }

    }
}

如果我使用EF来构建另一个控制器视图,例如对于一个Animal模型,这个新行将在 public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; } 下自动生成 - 如下所示:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }
        public System.Data.Entity.DbSet<WebApplication1.Models.Animal> Animals { get; set; }

    }
}

ApplicationDbContext (对于所有ASP.NET标识的东西)继承自 IdentityDbContext ,而 IdentityDbContext 继承自 DbContext . AllOtherStuffDbContext (对于我自己的东西)继承自 DbContext .

所以我的问题是:

我应该为所有其他自己的模型使用这两个中的哪一个( ApplicationDbContextAllOtherStuffDbContext )?或者我应该使用默认自动生成的 ApplicationDbContext ,因为它不应该是使用它的问题,因为它派生自基类 DbContext ,还是会有一些开销?你应该在你的应用程序中只为你的所有模型使用一个 DbContext 对象(我已在某处读过)所以我甚至不应该考虑在一个应用程序中同时使用 ApplicationDbContextAllOtherStuffDbContext ?或者使用ASP.NET身份的MVC 5中的最佳实践是什么?

4 回答

  • 7

    有很多关于 IdentityDbContext 的混淆,在Stackoverflow中快速搜索,你会发现这些问题:
    Why is Asp.Net Identity IdentityDbContext a Black-Box?
    How can I change the table names when using Visual Studio 2013 AspNet Identity?
    Merge MyDbContext with 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合并,我们有两个选择:

    First Option:
    创建一个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
    }
    

    Extra Notes:

    1)我们还可以使用以下解决方案更改asp.net Identity默认表名:

    public class ApplicationDbContext : IdentityDbContext
        {    
            public ApplicationDbContext(): base("DefaultConnection")
            {
            }
    
            protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<IdentityUser>().ToTable("user");
                modelBuilder.Entity<ApplicationUser>().ToTable("user");
    
                modelBuilder.Entity<IdentityRole>().ToTable("role");
                modelBuilder.Entity<IdentityUserRole>().ToTable("userrole");
                modelBuilder.Entity<IdentityUserClaim>().ToTable("userclaim");
                modelBuilder.Entity<IdentityUserLogin>().ToTable("userlogin");
            }
        }
    

    2)此外,我们可以扩展每个类并将任何属性添加到类'IdentityUser','IdentityRole',...

    public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
    {
        public ApplicationRole() 
        {
            this.Id = Guid.NewGuid().ToString();
        }
    
        public ApplicationRole(string name)
            : this()
        {
            this.Name = name;
        }
    
        // Add any custom Role properties/code here
    }
    
    
    // Must be expressed in terms of our custom types:
    public class ApplicationDbContext 
        : IdentityDbContext<ApplicationUser, ApplicationRole, 
        string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
    {
        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来扩展所有类 .

    Second Option: (不推荐)
    如果我们自己编写所有代码,我们实际上不必继承IdentityDbContext .
    所以基本上我们可以继承DbContext并从IdentityDbContext source code实现"OnModelCreating(ModelBuilder builder)"的自定义版本

  • 24

    如果你深入了解IdentityDbContext的抽象,你可以回答这个问题,但是如果你想要更多地控制创建的内容,那么对Identity包的依赖性要小一些have a look at my question and answer here . 如果您按照链接有一个代码示例,但总的来说,您只需将所需的DbSet添加到您自己的DbContext子类中 .

  • 6

    对于大家来说这是一个迟到的条目,但下面是我的实现 . 您还会注意到我已经删除了更改KEY默认类型的功能:有关详细信息,请参阅以下文章:

    NOTES:
    请注意,您不能使用 Guid's 作为密钥 . 这是因为在引擎盖下它们是 Struct ,因此没有取消装箱,这将允许它们从通用的 <TKey> 参数转换 .

    THE CLASSES LOOK LIKE:

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        #region <Constructors>
    
        public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess)
        {
        }
    
        #endregion
    
        #region <Properties>
    
        //public DbSet<Case> Case { get; set; }
    
        #endregion
    
        #region <Methods>
    
        #region
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            //modelBuilder.Configurations.Add(new ResourceConfiguration());
            //modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
        }
    
        #endregion
    
        #region
    
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    
        #endregion
    
        #endregion
    }
    
        public class ApplicationUser : IdentityUser<string, CustomUserLogin, CustomUserRole, CustomUserClaim>
        {
            #region <Constructors>
    
            public ApplicationUser()
            {
                Init();
            }
    
            #endregion
    
            #region <Properties>
    
            [Required]
            [StringLength(250)]
            public string FirstName { get; set; }
    
            [Required]
            [StringLength(250)]
            public string LastName { get; set; }
    
            #endregion
    
            #region <Methods>
    
            #region private
    
            private void Init()
            {
                Id = Guid.Empty.ToString();
            }
    
            #endregion
    
            #region public
    
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager)
            {
                // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
    
                // Add custom user claims here
    
                return userIdentity;
            }
    
            #endregion
    
            #endregion
        }
    
        public class CustomUserStore : UserStore<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
        {
            #region <Constructors>
    
            public CustomUserStore(ApplicationDbContext context) : base(context)
            {
            }
    
            #endregion
        }
    
        public class CustomUserRole : IdentityUserRole<string>
        {
        }
    
        public class CustomUserLogin : IdentityUserLogin<string>
        {
        }
    
        public class CustomUserClaim : IdentityUserClaim<string> 
        { 
        }
    
        public class CustomRoleStore : RoleStore<CustomRole, string, CustomUserRole>
        {
            #region <Constructors>
    
            public CustomRoleStore(ApplicationDbContext context) : base(context)
            {
            } 
    
            #endregion
        }
    
        public class CustomRole : IdentityRole<string, CustomUserRole>
        {
            #region <Constructors>
    
            public CustomRole() { }
            public CustomRole(string name) 
            { 
                Name = name; 
            }
    
            #endregion
        }
    
  • 145

    我会使用一个继承自IdentityDbContext的Context类 . 通过这种方式,您可以让上下文了解您的类与IdentityUser和IdentityDbContext的角色之间的任何关系 . IdentityDbContext中的开销非常小,它基本上是一个带有两个DbSet的常规DbContext . 一个用户,一个用于角色 .

相关问题