插入语句与 ApplicationUsers 上的 FK 约束冲突

Insert statement conflicts with FK constraint on ApplicationUsers

我有一个实体如下:

public class EntityX {
    public int Id { get; set; }
    ...
    [ForeignKey("Scheduled By")]
    public string ScheduledById { get; set; }
    public virtual ApplicationUser ScheduledBy { get; set; }
}

当我尝试向 table 中插入一个值时,出现以下错误:

"The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.EntityX_dbo.ApplicationUsers_ScheduledById". The conflict occurred in database "DB", table "dbo.ApplicationUsers", column 'Id'. The statement has been terminated."

首先想到的是 ApplicationUser table 是空的,因为 IdentityUser table (AspNetUsers) 包含所有值。但是,它的 TPH 和一个鉴别器列填充了 ApplicationUser table 名称。

我已验证在数据库中发送时填充了正确的 ID(即它对应于实际的用户 ID)但无法弄清楚为什么会这样。

提前谢谢你。干杯!

更新:

"Scheduled By" 中的 space 是一个拼写错误。它被错误地复制过来了。实际代码已按照 "ScheduledBy".

所指出的方式编写

更新 2:

问题似乎出在某处的上下文中。我有两个,一个从 DbContext 扩展而来的 DataContext,如下所示:

public class DataContext : DbContext
{
   public DbSet<EntityX> EntityXs { get; set; }
   ...
}

static DataContext()
{
     Database.SetInitializer<DataContext> (new CreateInitializer ());
}

public DataContext()
    : base("DataContext")
{

}

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

    modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
    modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
    modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
    ...

}

还有一个,从 IdentityDbContext 扩展如下:

public class SecurityContext : IdentityDbContext<ApplicationUser>
{
    static SecurityContext()
    {
        Database.SetInitializer<SecurityContext> (new CreateInitializer ());
    }

    public SecurityContext()
        : base("SecurityContext")
    {
        Database.Initialize(force: true);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<IdentityUser>()
            .ToTable("AspNetUsers");
        modelBuilder.Entity<ApplicationUser>()
            .ToTable("AspNetUsers");

        modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
        modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
        modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });

        base.OnModelCreating(modelBuilder);
    }

有了这个,配置就可以工作了……但是我遇到了一个问题,我有一个额外的 IdentityRole_Id 出现在 AspNetUserRoles table 中,如 [=55] 所述=]:EF Code First Migration unwanted column IdentityRole_Id. To work around that issue, I followed Hao Kung's advice here: Create ASP.NET Identity tables using SQL script 并以这种方式更改了我的上下文的 OnModelCreating 方法:

数据上下文:

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

    modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
    modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
    modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}

和 SecurityContext...

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

        var user = modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers");
        user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        user.Property(u => u.UserName).IsRequired();

        modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers"); //Needed?

        modelBuilder.Entity<IdentityUserRole>()
            .HasKey(r => new { r.UserId, r.RoleId })
            .ToTable("AspNetUserRoles");

        modelBuilder.Entity<IdentityUserLogin>()
            .HasKey(l => new { l.UserId, l.LoginProvider, l.ProviderKey })
            .ToTable("AspNetUserLogins");

        modelBuilder.Entity<IdentityUserClaim>()
            .ToTable("AspNetUserClaims");

        var role = modelBuilder.Entity<IdentityRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name).IsRequired();
        role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
}

虽然这样做可以正确构建数据库,但 AspNetUsers table 中的鉴别器列填充了 "ApplicationUser" 作为值,并且 AspNetUserRoles 中没有额外的列,任何插入用户 ID 的尝试FK 失败时将值写入 EntityX。

我完全迷路了。

如错误消息所示,您的外键需要与有效实体关联 属性。如果将 ForeignKey 属性放在外键 属性 上,则字符串参数表示关联导航的名称 属性。删除空格以匹配导航名称 属性:

public class EntityX
{
    public int Id { get; set; }
     ...
    [ForeignKey("ScheduledBy")]
    public string ScheduledById { get; set; }

    public virtual ApplicationUser ScheduledBy { get; set; }
}

使用这个:

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

  [ForeignKey("ScheduledBy")]
  public string ScheduledById { get; set; }


    [ForeignKey("ScheduledById")]
    [InverseProperty("EntityX_1")]
    public virtual ApplicationUser ScheduledBy{ get; set; }

}

public class ApplicationUser
{
   public string ScheduledById { get; set; }

   .
   .
   .
    [InverseProperty("ScheduledBy")]
    public virtual ICollection<EntityX> EntityX_1{ get; set; }       
}

事实证明,实体的设置方式没有任何问题。这是由于通过两个上下文迁移到同一个数据库时出现的一些问题。将它们合并为一个解决了这个问题。我已经在下面发布了我是如何解决问题的。希望这可以节省其他人的时间和痛苦。

public class SecurityContextContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<EntityX> EntityX { get; set; }
    ...

    static SecurityContext()
    {
        Database.SetInitializer<SecurityContext> (new CreateInitializer());
    }

    public SecurityContext()
        : base("SecurityContext")
    {
        Database.Initialize(force: true);
    }

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

        var user = modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers");
        user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        user.Property(u => u.UserName).IsRequired();
        user.HasKey(u => u.Id);

        var appUser = modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers"); //Needed?
        appUser.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        appUser.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        appUser.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        appUser.Property(u => u.UserName).IsRequired();

        modelBuilder.Entity<IdentityUserRole>()
            .HasKey(r => new { r.UserId, r.RoleId })
            .ToTable("AspNetUserRoles");

        modelBuilder.Entity<IdentityUserLogin>()
            .HasKey(l => new { l.UserId, l.LoginProvider, l.ProviderKey })
            .ToTable("AspNetUserLogins");

        modelBuilder.Entity<IdentityUserClaim>()
            .ToTable("AspNetUserClaims");

        var role = modelBuilder.Entity<IdentityRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name).IsRequired();
        role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);

    }

这个配置适合我。尽管 ApplicationUser table 没有通过此映射生成,但在 AspNetUsers table 中仍然创建了一个 Discriminator 列,其中填充了 "ApplicationUser"。 AspNetUsers table 还获取我在 ApplicationUser class 中定义的额外字段。 IdentityRole_Id 被消除,我能够分配角色并成功获得它们。 FK 问题也已解决。一切正常。