The use EF DbContext. My entity object has rowversion column (SQL Compact edition ver 4), which is used for concurrency check (ConcurrencyMode = Fixed, StoreGeneratedPattern=Computed).
To force concurrency exception, from UI I read the same table record in 2 different forms, edited each of them, and saved one after another. The following code does actual save operation.
When save button on the 2nd form clicked, concurrency error occurs as expected. However, the exception still persists at the second attempt, after copying original values from the database. Only third attempt succeeds without any error. Can someone explain me what might cause this problem?
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);
}
}
}
}
}
EDIT: I found that it occurs when I make both updates via UI. If in the above code, before first attempt, I do something the following:
var _ctx2 = new MyDbContext();
var myEntity = _ctx2.MyEntities.Where(ent => ent.Id == 2).Single();
myEntity.Name = "My new name";
_ctx2.SaveChanges();
_ctx2.Dispose();
then the code works as expected, given that another instance of myEntity was updated via UI; i.e second attempt will save myEntity. And, the problem lies at the following line:
foreach (var entry in exc.Entries)
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
because, when updated via UI, exc.Entries returns not the entity at which concurrency error happened, but its navigation property entity.
In this case, MyEntity is a tree-like self- referencing entity, which has two navigation properties: ParentEntity, and Children.
So, after the first save attempt, what I have in exc.Entries is ParentEntity (in a unchanged state), and only after the second save attempt, exc.Entries returns actual entity at which concurrency error thrown.
Side Note:
You can usecatch(DbUpdateConcurrencyException ex)
instead of checking the exception type usingif
. – NaveenBhat