Entity Framework - współbieżność

Każdy programista dostępu do danych napotyka trudności, odpowiadając na pytanie dotyczące współbieżności danych: „Co się stanie, jeśli więcej niż jedna osoba edytuje te same dane w tym samym czasie?”

  • Szczęśliwsi spośród nas mają do czynienia z regułami biznesowymi, które mówią „nie ma problemu, wygrywa ostatni”.

  • W tym przypadku współbieżność nie jest problemem. Bardziej prawdopodobne jest, że nie jest to takie proste i nie ma srebrnej kuli, która rozwiązałaby każdy scenariusz naraz.

  • Domyślnie Entity Framework przyjmuje ścieżkę „ostatnia w wygranych”, co oznacza, że ​​najnowsza aktualizacja jest stosowana, nawet jeśli ktoś inny zaktualizował dane między momentem pobrania danych a ich zapisaniem.

Weźmy przykład, aby lepiej to zrozumieć. Poniższy przykład dodaje nową kolumnę VersionNo w tabeli kursów.

Przejdź do projektanta i kliknij prawym przyciskiem myszy okno projektanta i wybierz aktualizuj model z bazy danych…

Zobaczysz, że kolejna kolumna została dodana do jednostki kursu.

Kliknij prawym przyciskiem myszy nowo utworzoną kolumnę VersionNo i wybierz Właściwości i zmień ConcurrencyMode na Fixed, jak pokazano na poniższej ilustracji.

Z ConcurrencyMode of Course.VersionNo ustawionym na Fixed, za każdym razem, gdy kurs jest aktualizowany, polecenie Update będzie szukać kursu przy użyciu jego EntityKey i jego właściwości VersionNo.

Rzućmy okiem na prosty scenariusz. Dwóch użytkowników pobiera ten sam kurs w tym samym czasie, a użytkownik 1 zmienia tytuł tego kursu na Matematyka i zapisuje zmiany przed użytkownikiem 2. Później, gdy użytkownik 2 zmienia tytuł tego kursu, który został pobrany przed zapisaniem zmian przez użytkownika 1, w tym przypadek użytkownik 2 otrzyma wyjątek współbieżności"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");
            }
         }
      }
   }
}