I have a MySQL Server which I access using Entity Framework 4.0. In the database I have a table called Works into which some counts. I develop web site with Asp.net. This table acccesable one more user same time. And this situation causes wrong incerement problem.
My code like that:
dbEntities myEntity = new dbEntities();
var currentWork = myEntity.works.Where(xXx => xXx.RID == 208).FirstOrDefault();
Console.WriteLine("Access work");
if (currentWork != null)
{
Console.WriteLine("Access is not null");
currentWork.WordCount += 5;//Default WordCount is 0
Console.WriteLine("Count changed");
myEntity.SaveChanges();
Console.WriteLine("Save changes");
}
Console.WriteLine("Current Count:" + currentWork.WordCount);
If one more than thread access the database same time, only last changes remain.
Current Output:
t1: Thread One - t2: Thread Two
t1: Access work
t2: Access work
t2: Access is not null
t1: Access is not null
t1: Count changed
t2: Count changed
t1: Save changes
t2: Save changes
t1: Current Count: 5
t2: Current Count: 5
Expected Output:
t1: Access work
t2: Access work
t2: Access is not null
t1: Access is not null
t1: Count changed
t2: Count changed
t1: Save changes
t2: Save changes
t1: Current Count: 5
t2: Current Count: 10
I know why apeear this problem, because this code is not atomic. How can i turn atomic operation?
With Entity Framework you can't make this an "atomic" operation. You have the steps:
In between these steps another client can load the entity from the database which still has the old value.
The best way to deal with this situation is to use optimistic concurrency. It basically means that the change in step 3 won't be saved if the counter is not the same anymore that it was when you loaded the entity in step 1. Instead you'll get an exception that you can handle by reloading the entity and reapplying the change.
The workflow would look like this:
Work
entity the WordCount
property must be marked as a concurrency token (annotations or Fluent API in case of Code-First)SaveChanges
in a try-catch
block and catch exceptions of type DbUpdateConcurrencyException
catch
block from the database, apply the change again and call SaveChanges
againIn this answer you can find an code example for this procedure (using DbContext
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With