【问题标题】:setDeleted loop resulting in modified entities in saveChanges callsetDeleted 循环导致在 saveChanges 调用中修改实体
【发布时间】:2017-03-08 22:54:22
【问题描述】:

我正在尝试删除对象的层次结构:

客户->订单->订单项->订单项选项

我设置了一个简单的嵌套循环,试图以“正确”的顺序执行操作,因为它们必须在数据库中完成 - 在删除父记录之前先删除子记录。

这是循环:

deleteCustomer(customer: Customer): Promise<void> {
  return this.getCustomerOrderHistory(customer.id).then(orders => {
    orders.forEach(o => {
      o.items.forEach(oi => {
        oi.options.forEach(opt => opt.entityAspect.setDeleted());
        oi.entityAspect.setDeleted();
      });
      o.entityAspect.setDeleted();
    });
    customer.entityAspect.setDeleted();
  });
}

问题是当每一层的父对象被setDeleted()时,会在EntityManager缓冲的变化中添加一组标记为“已修改”的重复实体记录。然后,当我调用 saveChanges 时,ASP.NET/EF 后端会引发异常,因为在执行与这些修改记录相对应的 UPDATE 语句时,DELETE 已经发生,因此 UPDATE 失败并出现 FK not found 类型的异常。

那么我在这里错过了什么?

【问题讨论】:

    标签: c# entity-framework typescript breeze


    【解决方案1】:

    这是一个在迭代数组时修改数组的经典案例。当我们像这样删除孩子时:

        o.items.forEach(oi => {
          oi.entityAspect.setDeleted();
        });
    

    ...每次我们调用setDeleted,它都会从父items 集合中移除子实体,也就是被迭代的集合。所以下一次迭代跳过了中间的实体,实际上只有大约一半的实体被设置为删除。

    这种情况的症状是 SaveBundle 包含已删除和已修改实体的混合,并且(如果您要删除父实体),当您尝试删除父实体而不删除所有实体时,数据库中的外键违规孩子们。

    简单的解决方案是在迭代之前制作数组的副本,例如使用slice:

        o.items.slice().forEach(oi => {
          oi.entityAspect.setDeleted();
        });
    

    【讨论】:

    • 谢谢史蒂夫,这正是问题所在。我知道 Breeze 可以通过设置 FK 属性(即 order.customerId)或 nav 属性(即 order.customer)或两者来添加或删除子/父关系,所以认为这样的事情正在发生,但不能找出原因。
    猜你喜欢
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 2013-10-15
    • 1970-01-01
    • 2013-05-04
    • 2016-01-30
    相关资源
    最近更新 更多