【问题标题】:Delete statement conflicts with Reference constraint删除语句与引用约束冲突
【发布时间】:2015-05-29 16:19:48
【问题描述】:

我认为这有一个非常简单的答案。我是实体框架的新手,我正在创建一个包含 ContactsGroups 作为对象/实体的测试应用程序。

这是我删除组的代码:

    private void button_DeleteGroup_Click(object sender, EventArgs e)
    {
        var _selectedGroup = dataGridView_Groups.SelectedRows[0].DataBoundItem as Group;

        try
        {
            cgContext.Groups.Remove(_selectedGroup);
            cgContext.SaveChanges();

            PopulateGroupGrid();
            MessageBox.Show("Successfully deleted group from database!");
        }
        catch(Exception ex) { MessageBox.Show("Failed to delete group from database.\r\n\r\n" + ex); }
    }

如果我删除联系人所属的组,以测试参照完整性,则会引发异常(应该如此):

"DELETE 语句与 REFERENCE 约束冲突 “FK_dbo.Contacts_dbo.Groups_Group_Id”。冲突发生在 数据库“ContactGroups”,表“dbo.Contacts”,列 'Group_Id'。声明已终止。”

然后我捕获这个异常并向用户显示一条消息。如果我然后去添加一个新的组或联系人或做任何事情,事务将失败并出现与以前相同的异常:

"DELETE 语句与 REFERENCE 约束冲突 “FK_dbo.Contacts_dbo.Groups_Group_Id”。冲突发生在 数据库“ContactGroups”,表“dbo.Contacts”,列 'Group_Id'。声明已终止。”

所以,当初始异常发生时,显然我没有清除/结束事务或其他东西。我做错了什么或错过了什么?

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    从实体框架中的无效实体状态回滚

    如果实体框架中存在约束违规或其他无效实体状态,则必须将状态恢复为有效/良好状态才能继续保持更改。

    This answer 详细描述了如何做到这一点。

    背景 - 为什么会发生 FK 约束冲突

    Contacts 表和 Groups 表之间存在外键约束。根据外键 (FK) FK_dbo.Contacts_dbo.Groups_Group_Id,每个联系人都有一个对组的引用。

    在上面的代码中,一个组被删除。在执行此操作之前,应进行检查以确保没有引用(位于)该组的联系人,或者应删除该组中的联系人。该方法将取决于您的业务逻辑。

    一旦删除了依赖项(即任何通过 Contacts.GroupId 引用组的联系人),就可以在不违反 FK 的情况下删除组。

    【讨论】:

    • 对不起,但这与发生的错误无关。我强迫它发生,以便我可以测试参照完整性。我想知道为什么在第一次抛出异常后,其他 CRUD 操作会继续抛出相同的异常。
    • 好的,所以你期待着异常。如果你说问题在于你不能回滚它以便测试其他更改,那么看看这个:stackoverflow.com/questions/5466677/…
    • 好的,谢谢,帮了大忙。这似乎应该有一个更标准化的答案。再次感谢:)
    • 当然。对不起,我误读了这个问题——我应该接受“应该”!
    【解决方案2】:

    将 WillCascadeOnDelete(true) 用于 WithOptional

      modelBuilder.Entity<Parent>()
      .HasMany<Child>(c => c.Children)
      .WithOptional(x => x.Parent)
      .WillCascadeOnDelete(true);
    

    使用包含

      var adv = db.Adv.Include(b => b.Features)
                      .Include(b => b.AdvDetails)
                      .Include(b => b.AdvGallery)
                      .FirstOrDefault(b => b.Id == id);
      db.Adv.Remove(adv);
    

    for .HasMany(...).WithMany(...) 包含就可以了

    【讨论】:

      【解决方案3】:

      实体框架也称为持久性存储。因为它保留实体的状态以及原始值和修改后的值。它实际上跟踪实体。

      EntityStateEntry 对 EF 中的每个实体都有条目。因此,当您调用cgContext.Groups.Remove(_selectedGroup) 时,它会将实体的状态更改为已删除。

      现在,您正在删除一个与它相关联的人的组。因此,参照完整性不会让您删除 _selectedGroup 并且您会收到错误消息。但_selectedGroup 的状态保留为Deleted。现在当你去添加一个新组时,添加组后你调用cgContext.SaveChange()。这会尝试保存更改 - 添加新组和删除 _selectedGroup。但同样,删除违反了 FK,你会得到同样的错误。

      要使其正常工作,您可以使用TransactionScope。查看用法here

      【讨论】:

      • 非常感谢您的回答 :) 我可能最终会使用它或创建这样的回滚方法: public void Rollback() { dataContext.Dispose(); dataContext=新的我的实体(你的连接); } transactionScope 是更好的方法吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-23
      • 1970-01-01
      • 2023-03-06
      • 2017-06-29
      • 1970-01-01
      相关资源
      最近更新 更多