xProgrammerx
xProgrammerx

Reputation: 13

Handling DbUpdateConcurrencyException by simply aborting all operations

I'm new to the EF 6 and i'm trying to implement optimistic concurrency control , simply by alerting the user of the conflict and aborting all operations, i'm not interested in analysing the conflict, i just want to abort.

And by "all operations" i mean that the action will not be just a simple update but will have many crud operations, should i use a transaction to implement them, or should the context.SaveChanges be enough?

I mean when i reach context.SaveChanges and the DbUpdateConcurrencyException is thrown are all other operations rolled back automatically or should i code it myself.

Here is an example of the relevant piece of code :

// Get the record for User 1.
var User1 = context1.Purchases.First();
// Get the record for User 2.
var User2 = context2.Purchases.First();
// Make a change and save it for User 1.
User1.Amount = Convert.ToDecimal(7.99);
context1.SaveChanges();
try
{
  // Make a change and save it for User 2.
  User2.Amount = Convert.ToDecimal(10.99);
  context2.SaveChanges();
}
catch (DbUpdateConcurrencyException DUCE)
{
// Here i just want to abort everything and notify the user .
}
// Display a success message.
MessageBox.Show("Update Succeeded!");
}

Thank you.

I consider using context.Database.BeginTransaction to controll the flow of operations.

Upvotes: 1

Views: 934

Answers (2)

xProgrammerx
xProgrammerx

Reputation: 13

So I guess my first problem about aborting all crud operations performed on the context is solved , because context.SaveChanges already implements a transaction, and as I understood it rolls back all operations when an exception happens, please note that I would be performing many operations on the context before calling .SaveChanges so if an exception happens they must all be rolled back. Also I would like to know if in case I need to use transactions one day is it safe to use context.Database.BeginTransaction.

So the only thing left is to handle the DbUpdateConcurrencyException by simply notifying the user about the conflict.

As for the "poisened" state of the DbContext, i guess that won't be a problem for me as I would be using it only for that action and then disposing of it (using it with a using statement).

So in my code i just have to show a messagebox in my catch for the DbUpdateConcurrencyException exception, operations are already rolled back by .SaveChanges.

// Get the record for User 1.
var User1 = context1.Purchases.First();
// Get the record for User 2.
var User2 = context2.Purchases.First();
// Make a change and save it for User 1.
User1.Amount = Convert.ToDecimal(7.99);
context1.SaveChanges();
try
{
  // Make a change and save it for User 2.
  User2.Amount = Convert.ToDecimal(10.99);
  context2.SaveChanges(); 
}
catch (DbUpdateConcurrencyException DUCE)
{
  MessageBox.Show("A concurrency conflict happened! please retry.");
}
// Display a success message.
MessageBox.Show("Update Succeeded!");
}

Thank you.

Upvotes: 0

Steve Py
Steve Py

Reputation: 34653

SaveChanges() already implements a Transaction across all changes so if you perform several updates on the DbContext and one of those updates fails for any reason, none of the updates will be committed, regardless of the exception that happens to get raised by SaveChanges().

The question then becomes what to do with the DbContext and the tracked entities. When a DbContext throws an exception on SaveChanges due to something like invalid data state (FK constraint violations, data too large, etc.) the DbContext is now in a "poisoned" state. This means that it is still tracking those entities with the invalid data and there isn't really any way to know which of potentially many entities with changes have a problem. The simple answer in these cases is to dispose the DbContext and recreate it before trying again. EF Core supports discarding instances from change tracking on a DbContext instance, where in EF6 you can manually detach tracked, modified or added instances. In both cases any modified instances need to be reloaded to revert any modifications otherwise they still potentially contained poisoned data if you try to Attach them to a new DbContext intance or use Update.

Upvotes: 0

Related Questions