EntityType“IdentityUserLogin”没有定义密钥。 定义这个EntityType的关键

我正在使用Entity Framework Code First和MVC 5.当我使用个人用户帐户身份validation创build了我的应用程序时,我获得了一个帐户控制器,并且还提供了使个人用户帐户身份validation正常工作所需的所有必需类和代码。

已经有的代码是这样的:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DXContext", throwIfV1Schema: false) { } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } } 

但是,我继续使用代码先创build自己的上下文,现在我也有以下几点:

 public class DXContext : DbContext { public DXContext() : base("DXContext") { } public DbSet<ApplicationUser> Users { get; set; } public DbSet<IdentityRole> Roles { get; set; } public DbSet<Artist> Artists { get; set; } public DbSet<Paintings> Paintings { get; set; } } 

最后,我有以下种子方法来添加一些数据,以便我在开发过程中一起工作:

 protected override void Seed(DXContext context) { try { if (!context.Roles.Any(r => r.Name == "Admin")) { var store = new RoleStore<IdentityRole>(context); var manager = new RoleManager<IdentityRole>(store); var role = new IdentityRole { Name = "Admin" }; manager.Create(role); } context.SaveChanges(); if (!context.Users.Any(u => u.UserName == "James")) { var store = new UserStore<ApplicationUser>(context); var manager = new UserManager<ApplicationUser>(store); var user = new ApplicationUser { UserName = "James" }; manager.Create(user, "ChangeAsap1@"); manager.AddToRole(user.Id, "Admin"); } context.SaveChanges(); string userId = ""; userId = context.Users.FirstOrDefault().Id; var artists = new List<Artist> { new Artist { FName = "Salvador", LName = "Dali", ImgURL = "160wpb4.jpg", UrlFriendly = "salvador-dali", Verified = true, ApplicationUserId = userId }, }; artists.ForEach(a => context.Artists.Add(a)); context.SaveChanges(); var paintings = new List<Painting> { new Painting { Title = "The Persistence of Memory", ImgUrl = "http://i62.tinypic.com/xx8tssn.jpg", ArtistId = 1, Verified = true, ApplicationUserId = userId } }; paintings.ForEach(p => context.Paintings.Add(p)); context.SaveChanges(); } catch (DbEntityValidationException ex) { foreach (var validationErrors in ex.EntityValidationErrors) { foreach (var validationError in validationErrors.ValidationErrors) { Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); } } } } 

我的解决scheme构build良好,但是当我尝试访问需要访问数据库的控制器时,出现以下错误:

DX.DOMAIN.Context.IdentityUserLogin :: EntityType“IdentityUserLogin”没有定义键。 定义这个EntityType的关键。

DX.DOMAIN.Context.IdentityUserRole :: EntityType“IdentityUserRole”没有定义键。 定义这个EntityType的关键。

我究竟做错了什么? 是因为我有两个上下文吗?

UPDATE

在阅读奥古斯托的答复后,我select了scheme3 。 下面是我的DXContext类现在的样子:

 public class DXContext : DbContext { public DXContext() : base("DXContext") { // remove default initializer Database.SetInitializer<DXContext>(null); Configuration.LazyLoadingEnabled = false; Configuration.ProxyCreationEnabled = false; } public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } public DbSet<Artist> Artists { get; set; } public DbSet<Painting> Paintings { get; set; } public static DXContext Create() { return new DXContext(); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<User>().ToTable("Users"); modelBuilder.Entity<Role>().ToTable("Roles"); } public DbQuery<T> Query<T>() where T : class { return Set<T>().AsNoTracking(); } } 

我还添加了一个User.cs和一个Role.cs类,他们看起来像这样:

 public class User { public int Id { get; set; } public string FName { get; set; } public string LName { get; set; } } public class Role { public int Id { set; get; } public string Name { set; get; } } 

我不确定是否需要用户的密码属性,因为默认的ApplicationUser有这个和其他一些字段!

无论如何,上述更改生成的罚款,但我又遇到这个错误,当应用程序运行:

无效的列名称UserId

UserId是我的Artist.cs上的整数属性

问题是你的ApplicationUserinheritance自IdentityUser ,它是这样定义的:

 IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser .... public virtual ICollection<TRole> Roles { get; private set; } public virtual ICollection<TClaim> Claims { get; private set; } public virtual ICollection<TLogin> Logins { get; private set; } 

并且它们的主键映射在IdentityDbContext类的OnModelCreating方法

 modelBuilder.Entity<TUserRole>() .HasKey(r => new {r.UserId, r.RoleId}) .ToTable("AspNetUserRoles"); modelBuilder.Entity<TUserLogin>() .HasKey(l => new {l.LoginProvider, l.ProviderKey, l.UserId}) .ToTable("AspNetUserLogins"); 

并且由于DXContext不是从它派生的,所以这些键不会被定义。

如果您深入了解Microsoft.AspNet.Identity.EntityFramework的来源 ,您将会了解一切。

我前段时间遇到过这种情况,我find了三种可能的解决scheme(可能还有更多):

  1. 针对两个不同的数据库或相同的数据库使用单独的DbContexts,但使用不同的表。
  2. 合并您的DXContext与ApplicationDbContext并使用一个数据库。
  3. 针对同一个表使用单独的DbContext,并相应地pipe理其迁移。

选项1:请参阅更新底部。

选项2:你将会得到一个像这样的DbContext:

 public class DXContext : IdentityDbContext<User, Role, int, UserLogin, UserRole, UserClaim>//: DbContext { public DXContext() : base("name=DXContext") { Database.SetInitializer<DXContext>(null);// Remove default initializer Configuration.ProxyCreationEnabled = false; Configuration.LazyLoadingEnabled = false; } public static DXContext Create() { return new DXContext(); } //Identity and Authorization public DbSet<UserLogin> UserLogins { get; set; } public DbSet<UserClaim> UserClaims { get; set; } public DbSet<UserRole> UserRoles { get; set; } // ... your custom DbSets public DbSet<RoleOperation> RoleOperations { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); // Configure Asp Net Identity Tables modelBuilder.Entity<User>().ToTable("User"); modelBuilder.Entity<User>().Property(u => u.PasswordHash).HasMaxLength(500); modelBuilder.Entity<User>().Property(u => u.Stamp).HasMaxLength(500); modelBuilder.Entity<User>().Property(u => u.PhoneNumber).HasMaxLength(50); modelBuilder.Entity<Role>().ToTable("Role"); modelBuilder.Entity<UserRole>().ToTable("UserRole"); modelBuilder.Entity<UserLogin>().ToTable("UserLogin"); modelBuilder.Entity<UserClaim>().ToTable("UserClaim"); modelBuilder.Entity<UserClaim>().Property(u => u.ClaimType).HasMaxLength(150); modelBuilder.Entity<UserClaim>().Property(u => u.ClaimValue).HasMaxLength(500); } } 

选项3:您将有一个DbContext等于选项2.让我们将其命名为IdentityContext。 而且你将会有另一个叫做DXContext的DbContext:

 public class DXContext : DbContext { public DXContext() : base("name=DXContext") // connection string in the application configuration file. { Database.SetInitializer<DXContext>(null); // Remove default initializer Configuration.LazyLoadingEnabled = false; Configuration.ProxyCreationEnabled = false; } // Domain Model public DbSet<User> Users { get; set; } // ... other custom DbSets public static DXContext Create() { return new DXContext(); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // IMPORTANT: we are mapping the entity User to the same table as the entity ApplicationUser modelBuilder.Entity<User>().ToTable("User"); } public DbQuery<T> Query<T>() where T : class { return Set<T>().AsNoTracking(); } } 

用户是:

 public class User { public int Id { get; set; } [Required, StringLength(100)] public string Name { get; set; } [Required, StringLength(128)] public string SomeOtherColumn { get; set; } } 

通过这个解决scheme,我将实体用户映射到与实体ApplicationUser相同的表。

然后,使用Code First Migrations,您将需要为这个DXContext生成IdentityContext和THEN的迁移,在Shailendra Chauhan撰写这篇伟大的文章之后: 使用多个数据上下文进行代码优先迁移

您将不得不修改为DXContext生成的迁移。 像这样的东西取决于ApplicationUser和User之间共享的属性:

  //CreateTable( // "dbo.User", // c => new // { // Id = c.Int(nullable: false, identity: true), // Name = c.String(nullable: false, maxLength: 100), // SomeOtherColumn = c.String(nullable: false, maxLength: 128), // }) // .PrimaryKey(t => t.Id); AddColumn("dbo.User", "SomeOtherColumn", c => c.String(nullable: false, maxLength: 128)); 

然后使用此自定义类从global.asax或应用程序的任何其他位置按顺序(首先是Identity迁移)运行迁移:

 public static class DXDatabaseMigrator { public static string ExecuteMigrations() { return string.Format("Identity migrations: {0}. DX migrations: {1}.", ExecuteIdentityMigrations(), ExecuteDXMigrations()); } private static string ExecuteIdentityMigrations() { IdentityMigrationConfiguration configuration = new IdentityMigrationConfiguration(); return RunMigrations(configuration); } private static string ExecuteDXMigrations() { DXMigrationConfiguration configuration = new DXMigrationConfiguration(); return RunMigrations(configuration); } private static string RunMigrations(DbMigrationsConfiguration configuration) { List<string> pendingMigrations; try { DbMigrator migrator = new DbMigrator(configuration); pendingMigrations = migrator.GetPendingMigrations().ToList(); // Just to be able to log which migrations were executed if (pendingMigrations.Any()) migrator.Update(); } catch (Exception e) { ExceptionManager.LogException(e); return e.Message; } return !pendingMigrations.Any() ? "None" : string.Join(", ", pendingMigrations); } } 

这样,我的n层交叉切割实体不会从AspNetIdentity类inheritance,因此我不必在每个使用它的项目中都导入这个框架。

很抱歉,广泛的职位。 我希望能就此提供一些指导。 我已经在生产环境中使用了选项2和3。

更新:展开选项1

对于最后两个项目,我使用了第一个选项:具有派生自IdentityUser的AspNetUser类和名为AppUser的单独自定义类。 在我的情况下,DbContexts分别是IdentityContext和DomainContext。 我这样定义了AppUser的Id:

 public class AppUser : TrackableEntity { [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] // This Id is equal to the Id in the AspNetUser table and it's manually set. public override int Id { get; set; } 

(TrackableEntity是我在我的DomainContext上下文的重写SaveChanges方法中使用的自定义抽象基类)

我首先创buildAspNetUser,然后创buildAppUser。 这种方法的缺点是你确保你的“CreateUser”function是事务性的(记住将有两个DbContext分别调用SaveChanges)。 使用TransactionScope出于某种原因并不适合我,所以我最终做了一件丑陋的事情,但这对我来说很有用:

  IdentityResult identityResult = UserManager.Create(aspNetUser, model.Password); if (!identityResult.Succeeded) throw new TechnicalException("User creation didn't succeed", new LogObjectException(result)); AppUser appUser; try { appUser = RegisterInAppUserTable(model, aspNetUser); } catch (Exception) { // Roll back UserManager.Delete(aspNetUser); throw; } 

(请,如果有人有更好的方式做这个部分我欣赏评论或build议编辑这个答案)

好处是你不需要修改迁移,你可以在AppUser上使用任何疯狂的inheritance层次结构,而不会干扰AspNetUser 。 实际上,我使用自动迁移为我的IdentityContext(从IdentityDbContext派生的上下文):

 public sealed class IdentityMigrationConfiguration : DbMigrationsConfiguration<IdentityContext> { public IdentityMigrationConfiguration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = false; } protected override void Seed(IdentityContext context) { } } 

这种方法也有避免让你的n层交叉实体从AspNetIdentity类inheritance的好处。

在我的情况下,我正确地inheritance了IdentityDbContext(使用我自己的自定义types和键定义),但无意中删除了对基类的OnModelCreating的调用:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // I had removed this /// Rest of on model creating here. } 

然后,从身份类中修复缺less的索引,然后我可以生成迁移并适当地启用迁移。

对于那些使用ASP.NET Identity 2.1并将主键从默认string更改为intGuid ,如果还有问题的话

EntityType'xxxxUserLogin'没有定义密钥。 定义这个EntityType的关键。

EntityType'xxxxUserRole'没有定义键。 定义这个EntityType的关键。

你可能忘了在IdentityDbContext上指定新的键types:

 public class AppIdentityDbContext : IdentityDbContext< AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim> { public AppIdentityDbContext() : base("MY_CONNECTION_STRING") { } ...... } 

如果你有

 public class AppIdentityDbContext : IdentityDbContext { ...... } 

甚至

 public class AppIdentityDbContext : IdentityDbContext< AppUser> { ...... } 

当您尝试添加迁移或更新数据库时,您将得到“没有键定义”的错误。

通过更改DbContext,如下所示;

  protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); } 

只需在OnModelCreating方法调用中添加base.OnModelCreating(modelBuilder); 它会好起来的 我正在使用EF6。

特别感谢#参议员

  protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); //foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) // relationship.DeleteBehavior = DeleteBehavior.Restrict; modelBuilder.Entity<User>().ToTable("Users"); modelBuilder.Entity<IdentityRole<string>>().ToTable("Roles"); modelBuilder.Entity<IdentityUserToken<string>>().ToTable("UserTokens"); modelBuilder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims"); modelBuilder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins"); modelBuilder.Entity<IdentityRoleClaim<string>>().ToTable("RoleClaims"); modelBuilder.Entity<IdentityUserRole<string>>().ToTable("UserRoles"); } } 

我的问题是类似的 – 我有一个新的桌子,我创build这个ahd以配合身份用户。 读完上面的答案后,意识到它与IsdentityUser和inheritance的特性有关。 我已经将Identity设置为自己的Context,所以为了避免将两者固定在一起,而不是使用相关的用户表作为真正的EF属性,我使用查询设置了非映射属性来获取相关实体。 (DataManager设置为检索OtherEntity所在的当前上下文。)

  [Table("UserOtherEntity")] public partial class UserOtherEntity { public Guid UserOtherEntityId { get; set; } [Required] [StringLength(128)] public string UserId { get; set; } [Required] public Guid OtherEntityId { get; set; } public virtual OtherEntity OtherEntity { get; set; } } public partial class UserOtherEntity : DataManager { public static IEnumerable<OtherEntity> GetOtherEntitiesByUserId(string userId) { return Connect2Context.UserOtherEntities.Where(ue => ue.UserId == userId).Select(ue => ue.OtherEntity); } } public partial class ApplicationUser : IdentityUser { public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> 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; } [NotMapped] public IEnumerable<OtherEntity> OtherEntities { get { return UserOtherEntities.GetOtherEntitiesByUserId(this.Id); } } }