Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity framework 5.0 handle optimistic concurrency exception?

When handling several potential exceptions during a context.SaveChanges() one of the exceptions is OptimisticConcurrency. Microsoft's documentation on this at http://msdn.microsoft.com/en-us/library/bb399228.aspx discusses this for EF 4.x ...

try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
        num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}

... but on EF 5.0 (RC), this doesn't seem to work because Refresh() doesn't exist on my EF5, code-first, DbContext derived context class.

I do see context.Entry(context.SalesOrderHeaders).Reload(); - but that appears to be a straightup reload-from-db and not a refresh/merge (with policy client wins).

Any ideas how to handle Optimistic concurrency exceptions in EF5? Actually even general pointers on exception handling in SaveChanges() would be nice

Thanks

like image 921
DeepSpace101 Avatar asked Aug 02 '12 04:08

DeepSpace101


People also ask

How do you handle optimistic concurrency exceptions?

To resolve optimistic concurrency conflicts, you can take advantage of the Reload method to update the current values in your entity residing in the memory with the recent values in the database. Once reloaded with the updated data, you can attempt to persist your entity again in the database.

How do you configure entity framework for optimistic concurrency?

If you do want to implement this approach to concurrency, you have to mark all non-primary-key properties in the entity you want to track concurrency for by adding the ConcurrencyCheck attribute to them. That change enables the Entity Framework to include all columns in the SQL WHERE clause of UPDATE statements.

Which concurrency approach is not supported in EF core?

Entity Framework Core provides no support for pessimistic concurrency control.


2 Answers

If your changes are only against the one entity (particular one row, not others tables etc), which is covered by the concurrency mechanism you are allowed to refresh context by disposing the old one and create new one. The thing is when the context is disposed every changed entities and not yet committed are detached from the context and the changes are lost. So be careful about the scope of your unit work!

    catch (DbUpdateConcurrencyException)
    {
        context.Dispose();
        context = new DBContext();
        Entity entity = context.Set<Entity>().Find(entityFromOldContext.Id);

        entity.Property1 = entityFromOldContext.Property1;
        entity.Property2 += 4;

        context.commit();
    }

In the Entity I use extra property for controlling concurrency as follows:

[Timestamp]
public Byte[] RowVersion { get; set; }

It is maybe not elegant way (and breaks UnitOfWork pattern), but it might be useful in some situations and finally an alternative to the above posts.

like image 30
Bronek Avatar answered Nov 11 '22 16:11

Bronek


The way how to solve concurrency exception in DbContext API reloads original entity:

catch (DbUpdateConcurrencyException ex)
{
    // Get failed entry
    var entry = ex.Entries.Single(...);
    // Overwrite original values with values from database but don't
    // touch current values where changes are held
    entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}

You should also be able to use the mentioned code but you must get ObjectContext instance from your DbContext instance (it is just a wrapper around ObjectContext).

catch (DbUpdateConcurrencyException ex)
{
    var objContext = ((IObjectContextAdapter)context).ObjectContext;
    // Get failed entry
    var entry = ex.Entries.Single(...);
    // Now call refresh on ObjectContext
    objContext.Refresh(RefreshMode.ClientWins, entry.Entity);        
}

You may even try:

objContext.Refresh(RefreshMode.ClientWins, ex.Entries.Select(e => e.Entity));
like image 52
Ladislav Mrnka Avatar answered Nov 11 '22 15:11

Ladislav Mrnka