Reputation: 11409
Scenario:
I have multiple accounts. Every account has a remaining credit. Every account corresponds to multiple users.
I want the RemainingCredit field to correctly reflect the remaining credit (I'm using EF to abstract away the db).
E.G. In a scenario where initial credit is X and 2 users log in, one spends A , and one spends B, I want the end sum to be X-A-B (order of read/modify/save should be irrelevant).
After reading up I think optimistic concurrency handling is what I'm looking for, as it scales well and this is very important to my scenario. The end procedure would be something like:
ComputeRemainingCredit(Double amountToBeDeducted) {
Account acc = context.Accounts.Where(condition)
ComputeRemainingCreditInternal(acc, amountToBeDeducted);
}
ComputeRemainingCreditInternal(Account acc, Double amountToBeDeducted) {
try {
acc.RemainingCredit = acc.RemainingCredit - amountToBeDeducted;
context.SaveChanges();
}
catch (OptimisticConcurrencyException ex) {
context.Refresh(RefreshMode.StoreWinds, acc);
//now I need to rerun the operation
ComputeRemainingCreditInternal(acc, amountToBeDeducted);
}
}
Upvotes: 2
Views: 3264
Reputation: 17994
As an alternative you can show an error message to the user saying that the operation failed because the data was modified, and then show him the current state of the data. The user would then decide to retry the operation if that was his/her intention.
You can find a good article about concurrency in Entity Framework here:
Handling Concurrency with the Entity Framework in an ASP.NET MVC Application
Upvotes: 0
Reputation: 33139
You need a field in your table that acts as a timestamp, or a version number, and use that for optimistic concurrency checking.
Say that you have a Version
field. Set that field's ConcurrencyMode
property to Fixed
, and then EF will do the optimistic concurrency checking for you and throw an OptimisticConcurrencyException
when necessary.
The alternative is to do this work yourself -- fetching the current record, verifying the field value against the one in the record you're updating, allowing or disallowing the update accordingly.
EDIT See also http://blogs.msdn.com/b/alexj/archive/2009/05/20/tip-19-how-to-use-optimistic-concurrency-in-the-entity-framework.aspx
EDIT More or less in pseudocode:
bool mustRetry = true;
while (mustRetry)
{
try
{
SpendTheMoney(context, parameters);
mustRetry = false;
} catch (OptimisticConcurrencyException exc)
{
// Do logging if you need, then just swallow the exception
}
}
Upvotes: 2