Entity Framework - Concurrency


Any data access developer faces difficulty while answering the question regarding data concurrency, “What happens if more than one person is editing the same data at the same time?”

  • The more fortunate among us deal with business rules that say “no problem, last one in wins.”

  • In this case, concurrency is not an issue. More likely, it’s not as simple as that, and there is no silver bullet to solve every scenario at once.

  • By default, the Entity Framework will take the path of “last one in wins,” meaning that the latest update is applied even if someone else updated the data between the time data was retrieved and the time data was saved.

Let’s take an example to understand it better. The following example adds a new column VersionNo in Course table.

Course Table

Go to the designer and right-click on the designer window and select update model from database…

Designer

You will see that another column is added in Course Entity.

Course Entity

Right-click on the newly created column VersionNo and select Properties and change the ConcurrencyMode to Fixed as shown in the following image.

New Created Column

With the ConcurrencyMode of Course.VersionNo set to Fixed, anytime a Course is updated, the Update command will look for the Course using its EntityKey and its VersionNo property.

Let’s take a look at a simple scenario. Two users retrieve the same course at the same time and user 1 changes the title of that course to Maths and saves changes before user 2. Later when user 2 changes the title of that course which was retrieved before user 1 save his changes, in that case user 2 will get concurrency exception "User2: Optimistic Concurrency exception occured".

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace DatabaseFirstDemo {

   class Program {

      static void Main(string[] args) {

         Course c1 = null;
         Course c2 = null;

         //User 1 gets Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c1 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 2 also get the same Course

         using (var context = new UniContextEntities()) {
            context.Configuration.ProxyCreationEnabled = false;
            c2 = context.Courses.Where(s ⇒ s.CourseID == 1).Single();
         }

         //User 1 updates Course Title
         c1.Title = "Edited from user1";

         //User 2 updates Course Title
         c2.Title = "Edited from user2";

         //User 1 saves changes first

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c1).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User1: Optimistic Concurrency exception occurred");
            }
         }

         //User 2 saves changes after User 1.
         //User 2 will get concurrency exection
         //because CreateOrModifiedDate is different in the database

         using (var context = new UniContextEntities()) {

            try {
               context.Entry(c2).State = EntityState.Modified;
               context.SaveChanges();
            } catch (DbUpdateConcurrencyException ex) {
               Console.WriteLine("User2: Optimistic Concurrency exception occurred");
            }
         }
      }
   }
}
Advertisements