C# 在SaveChanges之前添加相关实体时与外键冲突

C# 在SaveChanges之前添加相关实体时与外键冲突,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,如果我试图添加一个根本不存在的相关实体,我预计EF6.2.0会失败。但是在本例中,有问题的相关实体(Person)在SaveChanges之前被添加到上下文中(还不在Db中,但我看到它在ChangeManager中)。 但出于某种原因,EF告诉我:“INSERT语句与外键约束“FK\U AuditPerson\U Person”冲突。冲突发生在数据库“Person\”、表“Person.Person\”、列“Id”中。” 我在几节课上重复了我的问题: public class Person {

如果我试图添加一个根本不存在的相关实体,我预计EF6.2.0会失败。但是在本例中,有问题的相关实体(Person)在SaveChanges之前被添加到上下文中(还不在Db中,但我看到它在ChangeManager中)。 但出于某种原因,EF告诉我:“INSERT语句与外键约束“FK\U AuditPerson\U Person”冲突。冲突发生在数据库“Person\”、表“Person.Person\”、列“Id”中。”

我在几节课上重复了我的问题:

public class Person
{
    public Guid Id { get; set; }
}
而且:

public class AuditPerson
{
    public Guid AuditId { get; set; }
    public Guid PersonId { get; set; }
    public DateTime Timestamp { get; set; }
}
背景是:

public class PersonTestContext : DbContext
{
    public DbSet<Person> Packages { get; set; }
    public DbSet<AuditPerson> PersonAudits { get; set; }

    public PersonTestContext() : base("PersonTestDb")
    {
        Database.SetInitializer<PersonTestContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .ToTable("Person", "person")
            .HasKey(t => t.Id);

        modelBuilder.Entity<AuditPerson>()
            .ToTable("AuditPerson", "dbo")
            .HasKey(ap => new { ap.AuditId, ap.PersonId });

        base.OnModelCreating(modelBuilder);
    }
}
有什么替代方法可以告诉EF PersonId是指将在同一个SaveChanges调用中保存的对象

现在,当我添加以下内容时,问题就消失了:

public virtual Person Person { get; set; }
到AuditPerson类。但是如果没有导航属性,EF就不能解决这个问题吗

Db已经存在,且“FK_审核人”为:

我有一种感觉,我以前见过它没有导航属性

最后的想法

正如Steve所解释的,EF不保证插入的顺序。 如果我们使用相同的代码,但不是Person,类名是Appointment,则不会注意到任何问题:添加的记录没有任何FK冲突。
实际生产代码也发生了同样的情况(现已修复)。显然,EF按照类名对插入进行排序(查看代码可以澄清),因此人员插入是在审核之后完成的,但约会是在审核之前完成的,因此行为上存在差异。出现此问题的原因是没有实体之间的关系,EF不保证插入顺序与您的代码匹配。使用FK约束时,EF尝试首先插入AuditPerson记录并使FK跳闸。通过指定导航属性,EF可以计算出插入记录的顺序

如果您通常不从Person访问审核,那么我建议将Person引用放在审核和映射中,如下所示:

HasRequired(x => x.Person)
  .WithMany()
  .HasForeignKey(x => x.PersonId);
然后,在设置实体时:

context.Persons.Add(person);
var audit = new AuditPerson { AuditId = Guid.NewGuid(), Timestamp = DateTime.UtcNow, Person = person };

context.PersonAudits.Add(audit);
context.SaveChangesAsync().Wait();

另外,如果您的数据库是SQL Server,我建议您使用newsequentialId()使用数据库来管理PK,并设置EF以将它们识别为标识列。或者,您可以使用相同的hi/lo字节顺序生成GUID,以使UUID与SQL的顺序ID匹配。这些ID对索引更友好。

EF创建一个属性作为对Person对象的引用。在您的示例中,
AuditPerson
应该将属性设置为
Person
,而不是
Guid PersonId
。然后将新人员分配给其属性:
audit.Person=Person是,将人员导航属性添加到AuditPerson。
HasRequired(x => x.Person)
  .WithMany()
  .HasForeignKey(x => x.PersonId);
context.Persons.Add(person);
var audit = new AuditPerson { AuditId = Guid.NewGuid(), Timestamp = DateTime.UtcNow, Person = person };

context.PersonAudits.Add(audit);
context.SaveChangesAsync().Wait();