1

使用 EF DbContext。我的实体对象有 rowversion 列(SQL Compact edition ver 4),用于并发检查(ConcurrencyMode = Fixed,StoreGeneratedPattern=Computed)。

为了强制并发异常,我从 UI 中读取了 2 种不同形式的相同表记录,分别编辑它们,并一个接一个地保存。以下代码执行实际的保存操作。

单击第二个表单上的保存按钮时,按预期发生并发错误。但是,在从数据库复制原始值之后,第二次尝试时异常仍然存在。只有第三次尝试成功,没有任何错误。有人可以解释一下可能导致此问题的原因吗?

try
{
  _ctx.SaveChanges(); //first attempt
}
catch (Exception ex)
{
  if (ex is DbUpdateConcurrencyException)
  {
    var exc = ex as DbUpdateConcurrencyException;
    foreach (var entry in exc.Entries)
      entry.OriginalValues.SetValues(entry.GetDatabaseValues());
    try
    {
      _ctx.SaveChanges(); //second attempt
    }
    catch (Exception ex2)
    {
      if (ex2 is DbUpdateConcurrencyException)
      {
        var exc2 = ex2 as DbUpdateConcurrencyException;
        foreach (var entry in exc2.Entries)
          entry.OriginalValues.SetValues(entry.GetDatabaseValues());
        try
        {
          _ctx.SaveChanges(); //third attempt
        }
        catch (Exception ex3)
        {
          System.Windows.MessageBox.Show(ex3.Message);
        }
      }
    }
  }
}

编辑:我发现它发生在我通过 UI 进行两次更新时。如果在上面的代码中,在第一次尝试之前,我会执行以下操作:

var _ctx2 = new MyDbContext();
var myEntity = _ctx2.MyEntities.Where(ent => ent.Id == 2).Single();
myEntity.Name = "My new name";
_ctx2.SaveChanges();
_ctx2.Dispose();

然后代码按预期工作,因为 myEntity 的另一个实例是通过 UI 更新的;即第二次尝试将保存myEntity。而且,问题出在以下行:

foreach (var entry in exc.Entries)
  entry.OriginalValues.SetValues(entry.GetDatabaseValues());

因为,当通过 UI 更新时,exc.Entries 返回的不是发生并发错误的实体,而是它的导航属性实体。

在这种情况下,MyEntity 是一个树状自引用实体,它有两个导航属性:ParentEntity 和 Children。

因此,在第一次保存尝试之后,我在 exc.Entries 中拥有的是 ParentEntity(处于未更改状态),并且只有在第二次保存尝试之后,exc.Entries 返回引发并发错误的实际实体。

4

1 回答 1

1

好的,这似乎是一个 EF 错误。请参阅以下内容:

http://support.microsoft.com/kb/2390624#appliesto
http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/ce60bf40-cd05-42f6-ab8f-26b048ec83d7
于 2012-12-15T09:18:48.270 回答