使用 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 返回引发并发错误的实际实体。