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