【问题标题】:Insert foreign key records first to avoid racing condition?先插入外键记录以避免竞态条件?
【发布时间】:2021-07-30 23:29:42
【问题描述】:

我有 4 张桌子。

Table1 (primary)
Id

Table2 (foreign 1)
Id
Table1Id

Table3 (foreign 2)
Id
Table1Id

等等

Table1 也可以有没有 table2、3 和 4 的记录。

现在我们向这些表中添加了许多记录,我们的应用程序根据 Table1 中的 ID 进行提取以构建响应。

您通常会先在表 1 中添加记录,然后再在表 2 和 3 中添加记录等。

我们的问题是我们的轮询(每 30 秒运行一次)应用程序从 table1 中选择 Id,但无法从 Table2,3 和 4 中获取其链接记录,因为应用程序在链接记录保存在表 2,3 和 4 之前尝试获取在 table1 中保存记录后。

有没有办法防止这种情况发生?我们可以先保存外键记录吗?这样,应用程序不会在 table1 中看到记录,直到所有链接的记录都保存在 table2 和 3 中?这是一种常见且安全的做法吗?

任何建议都会很棒!

提前谢谢你

【问题讨论】:

  • 标记您实际使用的 dbms 引擎,因为任何“解决方案”都可能在很大程度上取决于这一事实。从逻辑上讲,您应该在事务中插入父行和子行,这足以防止出现此问题。但我闻到了 nolock 的使用——或者“组”没有事务。
  • 听起来您的保存过程需要使用事务(和 RCSI)。除了排除无效记录之外,您在选择方面无能为力

标签: sql-server entity-framework


【解决方案1】:

根据您的系统设计,您可能总是会遇到这种竞争条件。例如,在 Web 应用程序中,如果您有一个页面允许您添加 TableA,将其保存,然后单击按钮输入相关的 TableB,则在一段时间内对 TableA 的查询会收到不完整的表示。

解决方案是设计系统或数据,使其始终可以用来指示数据处于完整状态。例如,与其设计它以便独立保存关系中的逐页实体,不如赋予用户创建整个对象图的能力。实体框架与用于管理关系的导航属性一起使用,因此如果客户端进程遍历捕获表 A、相关表 B 和 C 等的数据,然后将所有这些详细信息传递到一个要持久化的结构,您可以进行一次“保存”操作,通过一次调用SaveChanges 一次性创建关联实体。这确保了所有实体都一起提交,或者根本不提交。 (如果有例外)EF 可以确保以正确的顺序填充表格并在需要的地方分配 FK。您无需“保存” TableA 即可将其 ID 填充到 TableB 中:

var tableA = new TableA
{
    Name = viewModel.Name,
    TableB = new TableB
    {
        Name = viewModel.BsName,
        // ... or use biewModel.BDetails.Name, etc.
    },
    TableC = new TableC
    {
        // ....
    }
};

context.TableAs.Add(tableA);
context.SaveChanges();  // Saves the A, B, and related C, etc.

如果数据相当大且复杂,并且尝试一次捕获所有内容没有意义,例如,如果数据可能在很长一段时间内输入,因为用户必须累积或检查数据等. 在记录状态被认为足够完整以供查询之前,您可以考虑在顶级表上使用诸如状态之类的东西。 (TableA) 这可能类似于枚举。当您最初创建 TableA 记录时,状态将默认为“InProgress”之类的内容。任何查询报告或类似的查看 TableAs 只会知道查询状态为“完成”的记录。当用户输入他们的表 B、C 等时,将会有一个自动验证或手动断言来确定是否可以将 TableA 记录标记为“完成”,从而更新状态。从那时起,报告/摘要查询视图将开始在结果中看到该行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-30
    • 2010-09-25
    • 1970-01-01
    • 2010-09-25
    • 2019-06-12
    • 1970-01-01
    • 2019-10-23
    相关资源
    最近更新 更多