EF DbContext を使用します。私のエンティティ オブジェクトには、同時実行チェック (ConcurrencyMode = Fixed、StoreGeneratedPattern = Computed) に使用される rowversion 列 (SQL Compact エディション バージョン 4) があります。
同時実行例外を強制するために、UI から同じテーブル レコードを 2 つの異なるフォームで読み取り、それぞれを編集して、次々に保存しました。次のコードは、実際の保存操作を行います。
2 番目のフォームの保存ボタンをクリックすると、予期したとおりに同時実行エラーが発生します。ただし、データベースから元の値をコピーした後、2 回目の試行では例外が引き続き発生します。3 回目の試行のみがエラーなしで成功します。誰かがこの問題の原因を説明できますか?
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 を介して更新された場合、コードは期待どおりに機能します。つまり、2 回目の試行で myEntity が保存されます。そして、問題は次の行にあります。
foreach (var entry in exc.Entries)
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
UI を介して更新すると、 exc.Entries は同時実行エラーが発生したエンティティではなく、そのナビゲーション プロパティ エンティティを返すためです。
この場合、MyEntity はツリー状の自己参照エンティティであり、ParentEntity と Children の 2 つのナビゲーション プロパティがあります。
したがって、最初の保存試行の後、exc.Entries にあるのは (変更されていない状態の) ParentEntity であり、2 回目の保存試行の後でのみ、exc.Entries は同時実行エラーがスローされた実際のエンティティを返します。