Entity Framework i optimistic concurrency

W przypadkach, gdy dane edytowane są dość rzadko i przez niewielką liczbę użytkowników, najczęściej stosowanym modelem blokowania danych do edycji jest… brak blokowania. Mówiąc konkretnie – chodzi o OCC, czyli Optimistic Concurrency Control.

Pytanie to – w wersji teoretycznej – poruszane było nie tak dawno na devpytaniach. Jeśli możemy założyć, że konflikty będą występować rzadko lub jest to akceptowalne w konkretnym przypadku, który mamy zaprogramować, użycie optymistycznego blokowania jest dobrym i prostym wyborem.
Optimistic concurrency nie blokuje rekordu przy jego pobieraniu, natomiast przy zapisie dokonywane jest nadmiarowe sprawdzenie, czy dane w bazie są tożsame z danymi, które były podstawą naszych zmian.

Stanąłem przed problemem obsługi optimistic concurrency przy korzystaniu z Entity Framework 4.0. Po krótkiej lekturze „Entity Framework 4.0 Recipes – A problem-solution approach” okazało się, że EF wspiera Optimistic Concurrency. Wystarczy dodać do bazy danych kolumnę informującą o – mówiąc ogólnie – wersji obiektu. Najłatwiej będzie przeanalizować problem na bardzo prostym przykładzie (kod do pobrania na końcu artykułu).

Załóżmy, że mamy tabelę przechowującą informacje o klientach:

Zacznijmy od dodania kolumny Timestamp, która pomoże nam w obsłudze OCC.

Teraz możemy wygenerować nasz model w EF. Tutaj następuje kluczowy krok – oznaczenie kolumny Timestamp jako odpowiedzialnej za sprawdzanie wersji obiektu.
W kolumnie Timestamp w naszym modelu musimy zmienić dwie właściwości:

  • StoreGeneratedPattern – na wartość computed. Podpowiadamy tutaj EF, że wartość tej kolumny nie będzie ustawiana przez nas a generowana za każdym razem gdy obiekt jest wstawiany lub aktualizowany.
  • Concurrency Mode – na wartość fixed. Ustawienie to oznacza, że oryginalna wartość tej kolumny będzie dołączona do warunku WHERE przy modyfikacji danych. Są to właściwie dwa przypadku – kasowanie oraz aktualizacja. Warunek taki wyglądał więc będzie mniej więcej tak: update table set …. where id = @id AND fixedColumn = @fixedColumnOriginalValue

Po wykonaniu tych prostych modyfikacji możemy przetestować nasz model i jego zachowanie w przypadku wystąpienia konfliktu.
Prosty kod testowy, który:

  • Pobiera dane klienta korzystając z jednego kontekstu EF, zmienia je ale nie utrwala z powrotem w bazie
  • Pobiera dane tego samego klienta, zmienia je i od razu utrwala korzystając z innego kontekstu.
  • Utrwala dane klienta z kroku pierwszego

Wynikiem działania tego kodu będzie rzucenie wyjątku w momencie wywołania ostatniej linii:

Entity Framework dzięki naszej kolumnie fixed typu timestamp wykrył, że nastąpiła modyfikacja obiektu między momentem pobrania go przez nas a chęcią wykonania zapisu. Wyjątku typu OptimisticConcurrencyException informuje nas o tym fakcie i pozwala obsłużyć tę sytuację. Wystarczy prosta modyfikacja ostatniej linii naszego kodu:

I problem optimistic concurrency rozwiązany.
Możemy również zaprząc do pomocy program Sql Server Profiler (nieoceniony przy pracy z Entity Framework) aby przekonać się, że wszystko wykonuje się dokładnie tak jak tego oczekiwaliśmy.

Tak wygląda pierwsza aktualizacja obiektu (ta nie powodująca wystąpienia wyjątku):

Tak natomiast ostatnia aktualizacja obiektu, czyli powodująca konflikt:

Jest to być może mało zauważalne, ale oba polecenia T-SQL jakie wygenerował dla nas EF modyfikują kolumnę Group różnymi wartościami („C” oraz „B”) jednocześnie wysyłając identyfikator rekordu (Id) oraz wartość kolumny Timestamp. Entity Framework sprawdza następnie ile rekordów zostało zmodyfikowanych poleceniem. Jeśli ta liczba wynosi zero – rzucany jest wspomniany wcześniej wyjątek.

Kod aplikacji testowej do pobrania: OptimisticConcurrencyDemo – VS 2010 Solution


Nie znaleziono podobnych wpisów.

Możesz śledzić odpowiedzi do tego wpisu za pomocą RSS 2.0 feed. Możesz leave a response, or trackback z Twojej własne strony.

1 Komentarz »

 
 

Dodaj komentarz

XHTML: Możesz użyć następujących tagów: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*