Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
471 views
in Technique[技术] by (71.8m points)

c# - Atomic Increment with Entity Framework

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

With Entity Framework you can't make this an "atomic" operation. You have the steps:

  1. Load entity from database
  2. Change counter in memory
  3. Save changed entity to database

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:

  • In the Work entity the WordCount property must be marked as a concurrency token (annotations or Fluent API in case of Code-First)
  • Load entity from database
  • Change counter in memory
  • Call SaveChanges in a try-catch block and catch exceptions of type DbUpdateConcurrencyException
  • If an exception occurs reload the entity in the catch block from the database, apply the change again and call SaveChanges again
  • Repeat the last step until no exception occurs anymore

In this answer you can find an code example for this procedure (using DbContext).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...