的EntityType'IdentityUserLogin“没有定义键。定义此的EntityType关键 [英] EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType

查看:3589
本文介绍了的EntityType'IdentityUserLogin“没有定义键。定义此的EntityType关键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我创建我的是个人用户帐户验证应用程序我与实体框架code首先和MVC 5.工作我获得了一个账户控制器和与它一起的所有所需的类和即需要得到用户逐张code帐户身份验证工作。

I am working with Entity Framework Code First and MVC 5. When I created my application with Individual User Accounts Authentication I was given an Account controller and along with it all the required classes and code that is needed to get the Indiv User Accounts authentication to work.

其中code已经到位是这样的:

Among the code already in place was this:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DXContext", throwIfV1Schema: false)
    {

    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

但后来我说干就干,首先使用code创建了自己的背景,所以我现在有太多以下内容:

But then I went ahead and created my own context using code first, so I now have the following too:

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

最后,我有以下种子的方法来添加一些数据,我与同时发展工作:

Finally I have the following seed method to add some data for me to work with whilst developing:

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 = "http://i62.tinypic.com/ss8txxn.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);
            }
        }
    }

}

我的解决方案建立很好,但是当我试图访问需要访问数据库,我得到以下错误的控制器:

My solution builds fine, but when I try and access a controller that requires access to the database I get the following error:

DX.DOMAIN.Context.IdentityUserLogin:的EntityType'IdentityUserLogin没有定义键。定义此的EntityType关键。

DX.DOMAIN.Context.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.

DX.DOMAIN.Context.IdentityUserRole:的EntityType'IdentityUserRole没有定义键。定义此的EntityType关键。

DX.DOMAIN.Context.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.

我是什么做错了吗?是不是因为我有两个背景?

What am I doing wrong? Is it because I have two contexts?

更新

阅读奥古斯托的答复后,我与选项3 去了。这里是我的DXContext类看起来像现在:

After reading Augusto's reply, I went with Option 3. Here is what my DXContext class looks like now:

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 类,它们看起来是这样的:

I also added a User.cs and a Role.cs class, they look like this:

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有和很多其他的领域!

I wasn't sure if I would need a password property on the user, since the default ApplicationUser has that and a bunch of other fields!

不管怎么说,上述变化建立很好,但我再次得到这个错误,当应用程序RAN:

Anyways, the above change builds fine, but again I get this error when the application is ran:

无效的列名用户ID

用户ID 是一个整数属性我 Artist.cs

推荐答案

的问题是,您的 ApplicationUser从IdentityUser ,它是这样定义继承:

The problem is that your ApplicationUser inherits from IdentityUser, which is defined like this:

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

and their primary keys are mapped in the method OnModelCreating of the class IdentityDbContext:

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没有从中获得,这些密钥没有得到界定。

and as your DXContext doesn't derive from it, those keys don't get defined.

如果你深入到<一个href=\"https://aspnetidentity.$c$cplex.com/SourceControl/latest#src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs\">sources的 Microsoft.AspNet.Identity.EntityFramework ,你就会明白一切。

If you dig into the sources of Microsoft.AspNet.Identity.EntityFramework, you will understand everything.

我碰到这种情况就前一段时间,我发现了三个可能的解决方案(也许还有更多):

I came across this situation sometime ago, and I found three possible solutions (maybe there are more):


  1. 使用单独的DbContexts针对两个不同的数据库或同一数据库,但不同的表。

  2. 您的合并与DXContext和ApplicationDbContext使用一个数据库。

  3. 使用单独的DbContexts对同一个表,并相应地管理他们的迁移。

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

Option 1: See update the the bottom.

选项2:
你最终会像这一个的DbContext:

Option 2: You will end up with a DbContext like this one:

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:

Option 3: You will have one DbContext equal to the option 2. Let's name it IdentityContext. And you will have another DbContext called DXContext:

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

其中user是:

where User is:

public class User
{
    public int Id { get; set; }

    [Required, StringLength(100)]
    public string Name { get; set; }

    [Required, StringLength(128)]
    public string SomeOtherColumn { get; set; }
}

通过这个解决方案,我映射实体的用户到同一个表作为实体ApplicationUser。

With this solution I'm mapping the entity User to the same table as the entity ApplicationUser.

然后,用code首先迁移,您需要生成用于IdentityContext的迁移和然后作为DXContext,继起赛伦德拉肖汉这个伟大的职位:<一href=\"http://www.dotnet-tricks.com/Tutorial/entityframework/2VOa140214-Entity-Framework-6-$c$c-First-Migrations-with-Multiple-Data-Contexts.html\">$c$c与第一次迁移多个数据上下文

Then, using Code First Migrations you'll need to generate the migrations for the IdentityContext and THEN for the DXContext, following this great post from Shailendra Chauhan: Code First Migrations with Multiple Data Contexts

您将不得不修改DXContext产生的迁移。这个东西取决于哪个属性ApplicationUser和用户之间共享,如:

You'll have to modify the migration generated for DXContext. Something like this depending on which properties are shared between ApplicationUser and 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或使用该自定义类的任何其他地方你的应用程序,以便在迁移(第一身份迁移):

and then running the migrations in order (first the Identity migrations) from the global.asax or any other place of your application using this custom class:

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类继承的,因此我不必在我使用它们每一个项目导入此框架。

This way, my n-tier cross cutting entities don't end up inheriting from AspNetIdentity classes, and therefore I don't have to import this framework in every project where I use them.

对不起,广泛的职位。我希望它可以提供一些这方面的指导。我已经使用选项2和3的生产环境。

Sorry for the extensive post. I hope it could offer some guidance on this. I have already used options 2 and 3 in production environments.

更新:扩展选项1

在过去的两个项目我已经使用了第一个选项:具有从IdentityUser派生的类AspNetUser,并呼吁APPUSER一个单独的自定义类。在我的情况下,DbContexts分别IdentityContext和DomainContext。而我所定义的APPUSER像这样的ID:

For the last two projects I have used the 1st option: having a AspNetUser class that derives from IdentityUser, and a separate custom class called AppUser. In my case, the DbContexts are IdentityContext and DomainContext respectively. And I defined the Id of the AppUser like this:

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方法使用自定义的抽象基类)

(TrackableEntity is custom abstract base class that I use in the overridden SaveChanges method of my DomainContext context)

我先创建AspNetUser,然后APPUSER。这种方法的缺点是,你确保你的CREATEUSER的功能是事务性的(记住,会有两个单独DbContexts的SaveChanges调用)。使用的TransactionScope没有为我工作由于某种原因,所以我最终做一些丑陋但对我的作品:

I first create the AspNetUser and then the AppUser. The drawback with this approach is that you have ensure that your "CreateUser" functionality is transactional (remember that there will be two DbContexts calling SaveChanges separately). Using TransactionScope didn't work for me for some reason, so I ended up doing something ugly but that works for me:

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

(请,如果有人自带做这一部分,我AP preciate评论或建议编辑这个答案的一个更好的方法)

(Please, if somebody comes with a better way of doing this part I appreciate commenting or proposing an edit to this answer)

的好处是,你不必修改迁移,您可以使用过任何APPUSER疯狂的继承层次结构,而不与AspNetUser 搞乱。而实际上我用的自动迁移为我IdentityContext(从IdentityDbContext派生的上下文中):

The benefits are that you don't have to modify the migrations and you can use any crazy inheritance hierarchy over the AppUser without messing with the AspNetUser. And actually I use Automatic Migrations for my IdentityContext (the context that derives from IdentityDbContext):

public sealed class IdentityMigrationConfiguration : DbMigrationsConfiguration<IdentityContext>
{
    public IdentityMigrationConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = false;
    }

    protected override void Seed(IdentityContext context)
    {
    }
}

这方法也有避免让你的n层交叉从AspNetIdentity类继承实体的利益。

This approach also has the benefit of avoiding to have your n-tier cross-cutting entities inheriting from AspNetIdentity classes.

这篇关于的EntityType'IdentityUserLogin“没有定义键。定义此的EntityType关键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆