1

我有两个表,一个表 A 是父表,另一个表 B 是子表(1:N 关系)。

如果我想在删除表A中的父时删除表B中的子记录,如果我不在数据库中使用级联删除,我会按照以下方式进行:

  1. 用户 A 在她的上下文中加载父记录,然后在上下文中加载所有子记录,将所有记录标记为已删除。
  2. 其他用户添加一个新的孩子。
  3. 用户 A 保存更改。由于第二个用户添加的寄存器不在她的上下文中,因此出现引用完整性错误。

在这种情况下,第一个用户可以在引发异常时再次尝试加载所有子级,但第三个用户可以添加一个新的子级并遇到同样的问题。理论上,这可能一直发生,第一个用户不会删除父级。

出于这个原因,我正在考虑使用事务来完成这项工作。

  1. 第一个用户开始交易。
  2. 第一个用户标记为已删除父级并保存更改。父级在数据库中被阻止。
  3. 第一个用户加载所有孩子,标记为已删除。
  4. 第一个用户保存更改。

在这种情况下,我有一个疑问,当父注册被第一个用户阻止时,这个父的ID可以被其他用户用作子记录的外键吗?还是数据库不允许使用ID,因为理论上是一条已删除的记录?

如果数据库不允许使用该 ID,则第二个用户在尝试添加新子代时会出现异常,以确保引用完整性,因为父代不存在。所以问题就解决了。不需要做任何其他事情。

但是,如果数据库允许使用被阻止父级的 ID,我需要做两件事:

在删除父级的方法中:

  1. 加载父级,将其标记为已删除并保存更改。这会阻止寄存器。
  2. 加载孩子,将它们标记为已删除并保存更改。
  3. 接受交易。

在添加孩子的方法中

  1. 加载父级。如果被阻塞,则该方法等待直到寄存器空闲。
  2. 添加新的孩子。
  3. 接受更改。

确实不需要在上下文中加载父级来删除子级,这将是对数据库的额外查询。

如果父级不存在,则获取异常并且不需要执行任何其他操作,用户现在尝试添加新寄存器是因为父级已被删除。

如果父级存在,则可以添加子级。

所以我有两个问题。

  1. 使用事务删除父记录以确保删除所有子记录是个好主意吗?

  2. 如果事务是一个不错的选择,当父级被阻塞时,数据库不允许使用父级ID添加新记录或者我需要在删除子级的方法中加载父级的解决方案?

4

1 回答 1

1

事务不会推迟删除操作。数据库中没有类似“标记为已删除”的内容。当您将实体标记为已删除并执行SaveChanges时,数据库中的记录将被删除。它总是发生在交易中。一旦记录即将被删除,就会检查 FK 约束,如果存在任何依赖记录,则会引发错误(除非ON DELETE约束规则未定义其他选项)。使用 FK 约束时,您必须始终在删除父级之前删除子级。

如果您的描述准确,您可以简单地使用您害怕的第一种方法并关闭查询并SaveChanges进入 single TransactionScope。它将以可序列化的隔离级别运行事务,这应确保在当前事务完成之前,没有其他事务能够将记录插入锁定的键范围(被查询锁定)。

虽然具有可序列化事务的解决方案可能会起作用,但它可能会带来其他问题 - 与应用程序中的其他事务发生死锁。因为重复失败的交易通常是最好的选择。如果您的系统具有如此大的并发负载,以至于删除事务可能“一直”失败,那么可能是时候彻底重新设计您的应用程序了。

于 2012-12-27T13:11:36.780 回答