【问题标题】:Getting a dirty read despite of having set isolation level to ReadCommitted尽管已将隔离级别设置为 ReadCommitted,但仍获得脏读
【发布时间】:2016-05-04 03:48:39
【问题描述】:

我有一个要同时执行的事务,当存在给定引用的条目时,我必须更新数据,否则我必须创建一条记录并相应地设置引用。到目前为止,我的代码是这样的:

using (var context = new MyDbContext())
{
    using (var scope = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
    {
         try
         {
             MyModel model = null;
             int hasEntry = context.MyModel.Where(x => x.refID == refID).Count();
             if (hasEntry == 0)
             {
                  model = new MyModel();
                  model.refID = refID;
             }
             else
             {
                  model = context.MyModel.Where(x => x.refID == refID).Single();
             }
             // process model object...
             if (hasEntry == 0)
             {
                  context.MyModel.Add(model);
             }
             else
             {
                  context.Entry(model).State = EntityState.Modified;
             }
             context.SaveChanges();
             scope.Commit();
         }
         catch(Exception)
         {
             scope.Rollback();
         }
    }
}

当两个(或更多)实例运行第一个实例时,插入但随后的实例不会更新。他们抛出一个 DBUpdateException 抱怨违反了 PK。

我不知道为什么那些必须更新的实际上是在尝试插入。他们得到一个已经添加到表中的 ID,因此他们抛出了异常。

AFAIK IsolationLevel.ReadCommitted 应该锁定行并在事务完成时释放它们。根据文档 (https://msdn.microsoft.com/en-us/library/cc546518.aspx) “如果您的连接使用已提交的读取隔离级别,并且 SQL Server 在执行 DML 语句时遇到脏行,它将等到当前拥有该行的事务已提交或回滚在继续执行之前。”

所以,我想我有一个脏读,因为实例没有等待首先插入的实例,所以他们尝试插入(使用已使用的 id)并且出现 PK 违规。

我错过了什么?

PD:这是我第一个使用 c# 和 entityframework 的项目。查了很多都没找到答案,如果问题有点傻,请见谅。

【问题讨论】:

    标签: c# asp.net entity-framework


    【解决方案1】:

    这不是脏读,而是检查使用时间问题 (TOCTOU) 该行在您检查时不存在,但在您插入之前已插入。

    防止这种情况的一种方法是使用某种锁定。您也可以尝试插入,如果失败则进行更新。

    【讨论】:

    • 你是对的,我通过应用这样的锁解决了这个问题: context.Database.ExecuteSqlCommand("SELECT TOP 1 ID FROM MyTable WITH (TABLOCKX, HOLDLOCK)") 在这里有更多关于那:stackoverflow.com/questions/13404061/…
    【解决方案2】:

    没有“要插入的脏行”之类的东西。当两个进程同时询问是否存在id=111 时,显然两者都会得到“否”。通常,应用程序管理的 PK 值插入应始终位于 try catch 块中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-23
      相关资源
      最近更新 更多