【问题标题】:Conflict with foreign key when the related entity is added before SaveChanges在 SaveChanges 之前添加相关实体时与外键冲突
【发布时间】:2018-08-16 15:59:07
【问题描述】:

如果我尝试添加一个根本不存在的相关实体,我预计 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 使插入按类名排序(查看代码可以澄清),因此人员插入是在审核之后完成的,但约会是在之前完成的,因此行为差异

【问题讨论】:

  • EF 创建一个属性作为对 Person 对象的引用。在您的示例中,AuditPerson 的属性应为Person,而不是Guid PersonId。然后将新的 Person 分配给它的属性:audit.person = person;
  • 是的,将 Person 导航属性添加到 AuditPerson。

标签: c# entity-framework entity-framework-6


【解决方案1】:

您遇到此问题的原因是,如果没有实体之间的关系,EF 不保证插入顺序与您的代码匹配。在 FK 约束下,EF 尝试首先插入 AuditPerson 记录并触发 FK。通过指定导航属性,EF 可以计算出插入记录的顺序。

如果您通常不从 Person 访问 Audits,那么我建议将 Person 引用放入 Audit 并映射为:

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 设置为将它们识别为身份列。或者,您可以使用相同的高/低字节顺序生成 GUID,以使 UUID 与 SQL 的顺序 ID 匹配。这些 ID 对索引更友好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 2019-10-28
    • 1970-01-01
    • 2012-09-06
    相关资源
    最近更新 更多