2010-12-15 10 views
28

Obiekty kontekstowe wygenerowane przez Entity Framework nie są wątkowo bezpieczne.Moduł Entity Framework Thread Bezpieczeństwo

Co się stanie, jeśli użyję dwóch oddzielnych kontekstów encji, po jednym dla każdego wątku (i zadzwonię pod numer SaveChanges()) - czy będzie to bezpieczne dla wątków?

// this method is called from several threads concurrently 
public void IncrementProperty() 
{ 
    var context = new MyEntities(); 

    context.SomeObject.SomeIntProperty++; 
    context.SaveChanges(); 
} 

wierzę kontekst ramy podmiot realizuje jakąś zmienną „przeciw”, który śledzi, czy aktualne wartości w kontekście są świeże, czy nie.

  1. Z powyższym kodem - wywołanym z oddzielnych wątków - czy nadal muszę blokować wokół przyrostów/zapisów?
  2. Jeśli tak, jaki jest preferowany sposób osiągnięcia tego w tym prostym scenariuszu?
+0

* "The kontekst obiektów generowanych przez Struktura Entity nie jest bezpieczna dla wątków. "* - dlaczego tak mówisz? – RPM1984

+0

Mam na myśli usługę MSDN: http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.aspx, gdzie jest napisane "Klasa ObjectContext nie jest bezpieczna dla wątków". – Harper

+0

Tak, to właśnie myślałem, że masz na myśli - kontekst, a nie byty. Dlatego nigdy nie powinieneś używać singletonu dla OC. – RPM1984

Odpowiedz

31

Więcej niż jeden wątek działający w jednym kontekście Entity Framework nie jest bezpieczny dla wątków.

Odrębna instancja kontekstu dla każdego wątku jest wątkowo bezpieczna. Dopóki każdy wątek wykonania ma swoją własną instancję kontekstu EF, wszystko będzie dobrze.

W swoim przykładzie możesz jednocześnie wywołać ten kod z dowolnej liczby wątków, a każdy z nich będzie pracował z własnym kontekstem.

jednak chciałbym zaproponować wdrożenie „używając” blok do tego, co następuje:

// this method is called from several threads concurrently 
public void IncrementProperty() 
{ 
    using (var context = new MyEntities()) 
    { 
     context.SomeObject.SomeIntProperty++; 
     context.SaveChanges(); 
    } 
} 
+0

Dzięki. Czy faktyczna wartość SomeIntProperty zostanie odczytana, gdy wykonasz SubmitChanges() lub o której godzinie? Mam na myśli to, że jeśli pierwszy wątek zwiększy wartość do 3, to czy drugi wątek będzie nadal mieć wartość 2 zapisaną w pamięci podręcznej, czy też pobierze wartość na miejscu? – Harper

+0

Przeoczyłem kilka ważnych aspektów twojego przykładu. Po pierwsze, nie masz kodu, który pobiera i wstawia "SomeObject". Tak jak pokazano, kod nie będzie działać. Nie ma współdzielenia pamięci podręcznych, buforów ani blokad między kontekstami. Tak więc każdy wątek z jego instancją kontekstu może być traktowany w ten sam sposób, w jaki myślimy, że proces działa na dwóch różnych maszynach. Musisz więc odpowiednio poradzić sobie z kwestiami współbieżności bazy danych. –

+1

Należy zaimplementować optymistyczną współbieżność lub transakcję bazy danych, aby poradzić sobie z problemami związanymi z bazą danych. –

0

Uważam, że "SomeObject.SomeIntProperty" jest statyczny. Nie ma to nic wspólnego z tym, że Encja jest bezpieczna dla wątków. Jeśli piszesz do zmiennych statycznych w środowisku wielowątkowym, powinieneś zawsze zawinąć je za pomocą podwójnej blokady, aby zapewnić bezpieczeństwo wątku.

0

Można stosować metodę fabryczną wstrzykiwać DbContext jako fabryka zamiast na przykład per se, spójrz na to : https://github.com/vany0114/EF.DbContextFactory

To bezpieczniejsze i unikasz twardego kodu tworzenia instancji w repozytoriach.

http://elvanydev.com/EF-DbContextFactory/

Jest rozszerzeniem Ninject to zrobić w bardzo prosty sposób, po prostu wywołanie metody kernel.AddDbContextFactory<YourContext>(); również trzeba zmienić swoje repozytorium przez otrzymanie Func<YourContext>