如果我尝试添加根本不存在的相关实体,我希望EF 6.2.0会失败。但是在这种情况下,有问题的相关实体(Person)在SaveChanges之前添加到上下文中(尚不在Db中,但我在ChangeManager中看到)。 但是由于某种原因,EF告诉我:“ INSERT语句与FOREIGN KEY约束\“ FK_AuditPerson_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);
}
}
下面的示例引发了问题:
static void Main(string[] args)
{
using (var context = new PersonTestContext())
{
var person = new Person { Id = Guid.NewGuid() };
context.Persons.Add(person);
var audit = new AuditPerson { AuditId = Guid.NewGuid(), Timestamp = DateTime.UtcNow, PersonId = package.Id };
context.PersonAudits.Add(audit);
context.SaveChangesAsync().Wait();
}
Console.ReadKey();
}
有什么可用的替代方法来告诉EF PersonId所引用的对象将保存在同一SaveChanges调用中?
现在,当我添加时问题就消失了
public virtual Person Person { get; set; }
到AuditPerson类。但是,如果没有导航属性,EF能否解决这个问题?
该Db已经存在,并且具有“ FK_AuditPerson_Person”为:
ALTER TABLE [dbo].[AuditPerson] WITH CHECK
ADD CONSTRAINT [FK_AuditPerson_Person]
FOREIGN KEY([PersonId])
REFERENCES [person].[Person] ([Id])
我觉得以前没有导航属性就看过了。
最终思想
正如史蒂夫(Steve)解释的那样,EF不保证插入顺序。 如果我们使用相同的代码,但是类名是Appointment,而不是Person,则不会出现任何问题:添加记录时不会出现任何FK冲突。 实际的生产代码(现已修复)也发生了同样的情况。显然,EF是按类名对插入进行排序的(可以澄清一下代码),因此Person插入是在Audits之后完成的,而约会是在Audit之前完成的,因此行为上的差异
答案 0 :(得分:0)
出现此问题的原因是,如果没有实体之间的关系,EF不能保证插入顺序将与您的代码匹配。由于FK约束,EF试图首先插入AuditPerson记录并跳出FK。通过指定导航属性,EF可以计算出插入记录的顺序。
如果您通常不从“人员”访问“审核”,那么建议您将“人员”引用放在“审核”中,并映射为:
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设置为将其识别为Identity列。或者,您可以使用相同的高/低字节顺序生成GUID,以使UUID与SQL的顺序ID匹配。这些ID更便于索引。