【问题标题】:Changing unique constrained column in EF Core using a transaction使用事务更改 EF Core 中的唯一约束列
【发布时间】:2017-08-23 04:07:15
【问题描述】:

我正在使用 EF Core(在 ASP.NET Core 中,我的 DbContext 存在于每个请求/范围内)。

我的Employee 实体有这个属性:

public bool? IsBoss { get; set; }

还有这个配置:

entityBuilder.Property(b => b.IsBoss).IsRequired(false);
entityBuilder.HasIndex(b => b.IsBoss).IsUnique();

这会创建一个过滤索引,因此只能有一个真,一个假,但可以有多个空值。

我的应用要求我始终只有一名员工使用IsBoss==true。

假设我想调换两名员工。

employee1.IsBoss = null;
employee2.IsBoss = true;
context.SaveChanges();

这会引发唯一的约束冲突异常。

我可以通过将其包装在事务中来解决此问题:

using (var transaction = context.BeginTransaction())
{
  try
  {
    employee1.IsBoss = null;
    context.SaveChanges();

    employee2.IsBoss = true;
    context.SaveChanges();

    transaction.Commit();
  }
  catch
  {
    transaction.Rollback();
  }
}

我的问题是:为什么第一种方法会失败?我认为 EF Core automatically wraps 事务中的所有内容。为什么我必须使用事务?

【问题讨论】:

    标签: .net entity-framework transactions entity-framework-core unique-constraint


    【解决方案1】:

    第一种方法由于与交易不同的原因而失败。 Tracking issue 在 EF repo 上,涵盖与您相同的场景。

    当调用SaveChanges 时,EF 处理更改并计算要发送到数据库的命令。这组命令可以具有依赖性。就像您的情况一样,您需要先将 null 的值设置为 employee1,然后才能将值设置为 true 的 employee2。 EF 对命令进行排序以找出它们需要执行的顺序。 EF 根据外键约束进行了这种排序。但正如该问题报告的那样,我们没有考虑唯一索引,因此命令以错误的顺序发送,导致违反唯一约束。

    该问题已在当前代码库中得到修复。它将在下一个公开版本中提供。同时,作为一种解决方法,您需要像在第二个代码中那样调用SaveChanges 两次。多次调用SaveChanges,您可以控制发送到数据库的命令的顺序。除非您希望这两个更改都是一个原子操作,否则您不需要将其包装在事务中。每个SaveChanges 都有自己的事务,除非用户启动一个。

    【讨论】:

    • 感谢详细解答...没想到竟然是个bug!你是说下一个版本将允许我这样做 1) 只有一个 SaveChanges() 和 2) 没有事务?
    • 这两个问题的答案都是肯定的 :) 您只需要一个 SaveChanges 并且默认情况下它将包含在事务中。
    猜你喜欢
    • 2021-06-24
    • 2018-09-14
    • 1970-01-01
    • 2018-10-08
    • 2020-10-12
    • 2017-04-07
    • 2020-07-22
    • 2022-01-23
    • 2022-11-19
    相关资源
    最近更新 更多