OptimisticConcurrencyException

OptimisticConcurrencyException

更新(2010-12-21):根据我一直在做的测试,完全重写了这个问题。另外,这曾经是POCO特定的问题,但事实证明,我的问题不一定是POCO特定的。

我正在使用Entity Framework,并且我的数据库表中有一个timestamp列,该列应用于跟踪乐观并发的更改。我已在实体设计器中将此属性的并发模式设置为“固定”,并且得到的结果不一致。以下是几个简化的场景,它们演示了并发检查在一种情况下有效,而在另一种情况下无效。

成功引发OptimisticConcurrencyException:

如果我附加了断开连接的实体,那么如果存在时间戳冲突,则SaveChanges将引发OptimisticConcurrencyException:

    [HttpPost]
    public ActionResult Index(Person person) {
        _context.People.Attach(person);
        var state = _context.ObjectStateManager.GetObjectStateEntry(person);
        state.ChangeState(System.Data.EntityState.Modified);
        _context.SaveChanges();
        return RedirectToAction("Index");
    }

不引发OptimisticConcurrencyException:

另一方面,如果我从数据库中检索到我的实体的新副本,并在某些字段上进行了部分更新,然后调用SaveChanges(),那么即使存在时间戳冲突,也不会获得OptimisticConcurrencyException :
    [HttpPost]
    public ActionResult Index(Person person) {
        var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
        currentPerson.Name = person.Name;

        // currentPerson.VerColm == [0,0,0,0,0,0,15,167]
        // person.VerColm == [0,0,0,0,0,0,15,166]
        currentPerson.VerColm = person.VerColm;

        // in POCO, currentPerson.VerColm == [0,0,0,0,0,0,15,166]
        // in non-POCO, currentPerson.VerColm doesn't change and is still [0,0,0,0,0,0,15,167]
        _context.SaveChanges();
        return RedirectToAction("Index");
    }

基于SQL Profiler,看来Entity Framework忽略了新的VerColm(这是timestamp属性),而是使用了最初加载的VerColm。因此,它将永远不会引发OptimisticConcurrencyException。

更新:应Jan的请求添加其他信息:

请注意,我还在上面的代码中添加了注释,以使其与在执行本示例时在 Controller 操作中看到的内容一致。

这是更新之前我的数据库中VerColm的值:0x0000000000000FA7

这是执行更新时SQL Profiler显示的内容:
exec sp_executesql N'update [dbo].[People]
set [Name] = @0
where (([Id] = @1) and ([VerColm] = @2))
select [VerColm]
from [dbo].[People]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 nvarchar(50),@1 int,@2 binary(8)',@0=N'hello',@1=1,@2=0x0000000000000FA7

请注意,@ 2应该是0x0000000000000FA6,但它是0x0000000000000FA7

更新后,这是我数据库中的VerColm:0x0000000000000FA8

有谁知道我该如何解决这个问题?我希望Entity Framework在更新现有实体时发生异常,并且存在时间戳冲突。

谢谢

最佳答案

说明

在第二个代码示例中未获得预期的OptimisticConcurrencyException的原因是由于EF检查并发性的方式:

通过查询数据库检索实体时,EF会在查询时将所有带有ConcurrencyMode.Fixed标记属性的值记为原始的未修改值。

然后,您更改一些属性(包括标记为Fixed的属性)并在DataContext上调用SaveChanges()

EF通过将所有Fixed标记的db列的当前值与Fixed标记的属性的原始未修改值进行比较,检查并发更新。
这里的关键点是,EF将时间戳属性的更新视为常规数据属性的更新。您看到的行为是设计使然。

解决方案/解决方法

要解决此问题,您可以使用以下选项:

  • 使用第一种方法:不要为实体重新查询数据库,而是将重新创建的实体附加到您的上下文中。
  • 将您的时间戳记值设为当前的db值,以便EF并发检查使用您提供的值,如下所示(有关类似问题,另请参见this answer):
    var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
    currentPerson.VerColm = person.VerColm; // set timestamp value
    var ose = _context.ObjectStateManager.GetObjectStateEntry(currentPerson);
    ose.AcceptChanges();       // pretend object is unchanged
    currentPerson.Name = person.Name; // assign other data properties
    _context.SaveChanges();
    
  • 您可以通过比较时间戳记值和重新查询的时间戳记值来自己检查并发性:
    var currentPerson = _context.People.Where(x => x.Id == person.Id).First();
    if (currentPerson.VerColm != person.VerColm)
    {
        throw new OptimisticConcurrencyException();
    }
    currentPerson.Name = person.Name; // assign other data properties
    _context.SaveChanges();
    
  • 关于c# - 在某些情况下,OptimisticConcurrencyException在 Entity Framework 中不起作用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4402586/

    10-13 07:08