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

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

问题描述

我正在使用Entity Framework Code First和MVC 5.当我使用个人用户帐户身份验证创建我的应用程序时,我被赋予了一个帐户控制器,并附带了所有必需的类和代码需要让Indiv用户帐户身份验证正常工作。



已经存在的代码是:

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

}

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

但是,我继续创建了自己的上下文代码第一,所以我现在也有以下:

  public class DXContext:DbContext 
{
public DXContext():base(DXContext)
{

}

public DbSet< ApplicationUser>用户{get;组; }
public DbSet< IdentityRole>角色{get;组; }
public DbSet< Artist>艺术家{get;组; }
public DbSet<绘画>绘画{get;组; }
}

最后我有以下种子方法添加一些数据供我工作同时开发:

  protected override void Seed(DXContext context)
{
try
{

if(!context.Roles.Any(r => r.Name ==Admin))
{
var store = new RoleStore< IdentityRole>上下文);
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> );
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已验证= true,ApplicationUserId = userId},
};

artists.ForEach(a => context.Artists.Add(a));
context.SaveChanges();

var painting = new List<绘画>
{
new绘画{Title =持久记忆,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);
}
}
}

}

我的解决方案构建良好,但是当我尝试访问需要访问数据库的控制器时,会收到以下错误:


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



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


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



更新



阅读Augusto的回覆我选择了选项3 。这是DXContext类现在的样子:

  public class DXContext:DbContext 
{
public DXContext ():base(DXContext)
{
//删除默认初始化程序
Database.SetInitializer< DXContext>(null);
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;

}

public DbSet< User>用户{get;组; }
public DbSet< Role>角色{get;组; }
public DbSet< Artist>艺术家{get;组; }
public DbSet<绘画>绘画{get;组; }

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>()其中T:class
{
return Set< T>()。AsNoTracking();
}
}

我还添加了一个用户.cs 和一个 Role.cs 类,它们如下所示:

  public class User 
{
public int Id {get;组; }
public string FName {get;组; }
public string LName {get;组;
}

public class Role
{
public int Id {set;得到; }
public string Name {set;得到;
}

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



无论如何,上面的更改构建正常,但是当应用程序运行时,我再次收到此错误: / p>


无效列名UserId


UserId 是我的 Artist.cs

上的整数属性解决方案

问题是您的 ApplicationUser继承自IdentityUser ,其定义如下:

  IdentityUser:IdentityUser< string,IdentityUserLogin,IdentityUserRole,IdentityUserClaim>,IUser 
....
public virtual ICollection< TRole>角色{get;私人集合}
public virtual ICollection< TClaim>索赔{get;私人集合}
public virtual ICollection< TLogin>登录{get;私人集合}

,它们的主键映射到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不派生,所以这些键不被定义。 p>

如果您浏览来源 Microsoft.AspNet.Identity.EntityFramework ,您将了解所有内容。



我曾经遇到过这种情况,我发现了三种可能的解决方案(也许还有更多的):


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

  2. 将DXContext与ApplicationDbContext合并并使用一个数据库。

  3. 使用单独的DbContext表格并相应地管理他们的迁移。

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



选项2:
您最终将一个像这样的DbContext:

  public class DXContext:IdentityDbContext< User,Role,
int,UserLogin,UserRole, UserClaim> //:DbContext
{
public DXContext()
:base(name = DXContext)
{
Database.SetInitializer< DXContext>(null) ; //删除默认初始化程序
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}

public static DXContext Create()
{
返回新的DXContext();
}

//身份和授权
public DbSet< UserLogin> UserLogins {get;组; }
public DbSet< UserClaim> UserClaims {get;组; }
public DbSet< UserRole> UserRoles {get;组; }

// ...您的自定义DbSets
public DbSet< RoleOperation> RoleOperations {get;组;

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Conventions.Remove< PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove< OneToManyCascadeDeleteConvention>();

//配置Asp Net身份表
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)//应用程序配置文件中的连接字符串。
{
Database.SetInitializer< DXContext>(null); //删除默认初始化程序
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}

//域模型
public DbSet< User>用户{get;组; }
// ...其他自定义DbSets

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

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Conventions.Remove< PluralizingTableNameConvention>();

//重要信息:我们将实体用户映射到实体ApplicationUser
modelBuilder.Entity< User>()。ToTable(User);
}

public DbQuery< T> Query< T>()其中T:class
{
return Set< T>()。AsNoTracking();
}
}

其中用户是:

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

[必需,StringLength(100)]
public string Name {get;组; }

[必需,StringLength(128)]
public string SomeOtherColumn {get;组; }
}

使用此解决方案,我将实体用户映射到同一个表格实体ApplicationUser。



然后,使用代码优先迁移,您需要为IdentityContext生成迁移,而 THEN 具有多个数据上下文的代码首次迁移



您必须修改生成的迁移对于DXContext。这样的事情取决于ApplicationUser和User之间共享哪些属性:

  // CreateTable(
//dbo .User,
// c => new
// {
// Id = c.Int(nullable:false,identity:true),
//名称= 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或任何其他应用程序使用这个自定义类的地方:

  public static class DXDatabaseMigrator 
{
public static string ExecuteMigrations ()
{
return string.Format(Identity migrations:{0}。DX migrations:{1}。,ExecuteIdentityMigrations(),
ExecuteDXMigrations());
}

私有静态字符串ExecuteIdentityMigrations()
{
IdentityMigrationConfiguration configuration = new IdentityMigrationConfiguration();
return RunMigrations(configuration);
}

私有静态字符串ExecuteDXMigrations()
{
DXMigrationConfiguration configuration = new DXMigrationConfiguration();
return RunMigrations(configuration);
}

私有静态字符串RunMigrations(DbMigrationsConfiguration配置)
{
列表< string> pendingMigrations;
try
{
DbMigrator migrator = new DbMigrator(configuration);
pendingMigrations = migrator.GetPendingMigrations()。ToList(); //只是为了记录执行的迁移

if(pendingMigrations.Any())
migrator.Update();
}
catch(异常e)
{
ExceptionManager.LogException(e);
return e.Message;
}
return!pendingMigrations.Any()? None:string.Join(,,pendingMigrations);
}
}

这样,我的n层交叉切割实体不要继承AspNetIdentity类,所以我不必在我使用它的每个项目中导入这个框架。



对于广泛的帖子,对不起。我希望能为此提供一些指导。我已经在生产环境中使用了选项2和3。



更新:展开选项1



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

  public class AppUser:TrackableEntity 
{
[ Key,DatabaseGenerated(DatabaseGeneratedOption.None)]
//这个Id等于AspNetUser表中的Id,它是手动设置的。
public override int Id {get;组; (TrackableEntity是我在DomainContext上下文的重写SaveChanges方法中使用的自定义抽象基类)



<



我首先创建AspNetUser,然后创建AppUser。这种方法的缺点是您确保您的CreateUser功能是事务性的(请记住,将有两个DbContexts单独调用SaveChanges)。因为某些原因,使用TransactionScope对我来说没有效果,所以我最终做了一些丑陋的事情,但对我来说很有用:

  IdentityResult identityResult = UserManager.Create(aspNetUser,model.Password); 

如果(!identityResult.Succeeded)
抛出新的TechnicalException(用户创建没有成功,新的LogObjectException(结果));

AppUser appUser;
try
{
appUser = RegisterInAppUserTable(model,aspNetUser);
}
catch(异常)
{
//回滚
UserManager.Delete(aspNetUser);
throw;
}

(请,如果有人有更好的方式做这部分我很感激评论或提出对此答案的编辑)



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

 公共密码类IdentityMigrationConfiguration:DbMigrationsConfiguration< IdentityContext> ; 
{
public IdentityMigrationConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
}

protected override void Seed(IdentityContext context)
{
}
}

这种方法也有利于避免让您的n层交叉实体继承自AspNetIdentity类。


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.

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

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' has no key defined. Define the key for this 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?

UPDATE

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

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

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!

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

Invalid Column name UserId

UserId is an integer property on my Artist.cs

解决方案

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

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");

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

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. Use separate DbContexts against two different databases or the same database but different tables.
  2. Merge your DXContext with ApplicationDbContext and use one database.
  3. Use separate DbContexts against the same table and manage their migrations accordingly.

Option 1: See update the the bottom.

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

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

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

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

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

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

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

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.

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.

UPDATE: Expand Option 1

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 is custom abstract base class that I use in the overridden SaveChanges method of my DomainContext context)

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

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

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)
    {
    }
}

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天全站免登陆