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");
}
}
}
}
}