使用事务

时间:2017-03-29 15:08:33

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

我正在使用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();

这会创建一个过滤索引,因此只能有一个true,一个false,但很多空。

我的应用要求我始终只有一名员工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在交易中的一切。为什么我必须使用交易?

1 个答案:

答案 0 :(得分:1)

第一种方法由于与事务不同的原因而失败。 EF repo上的Tracking issue,涵盖了与您相同的方案。

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

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