setDeleted 循环导致在 saveChanges 调用中修改实体

setDeleted loop resulting in modified entities in saveChanges call

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

客户->订单->OrderItems->OrderItemOptions

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

这是循环:

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()时,一组重复的实体记录被标记为"modified"被添加到EntityManager缓冲的变化中。然后,当我调用 saveChanges 时,ASP.NET/EF 后端抛出异常,因为在执行与这些修改记录相对应的 UPDATE 语句时,DELETE 已经发生,因此 UPDATE 失败并返回 FK not发现某种异常。

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

这是在遍历数组的同时修改数组的经典案例。当我们像这样删除 children 时:

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

…每次我们调用setDeleted,它都会从parentitems集合中移除child实体,这是被迭代的集合。所以下一次迭代会跳过中间的实体,实际上只有大约一半的实体被设置为删除。

这将是一个包含已删除和已修改实体混合的 SaveBundle,并且(如果您正在删除 parent 实体),当您尝试删除时,数据库中的外键违规parent而不删除所有children.

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

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