Entity Framework i pula połączeń

268

Niedawno zacząłem używać Entity Framework 4.0 w mojej aplikacji .NET 4.0 i jestem ciekawy kilku rzeczy związanych z pulowaniem.

  1. Pule połączeń, jak wiem, są zarządzane przez dostawcę danych ADO.NET, w moim przypadku serwera MS SQL. Czy dotyczy to tworzenia nowego kontekstu encji ( ObjectContext), tzn. Parametru bez parametrów new MyDatabaseModelEntities()?

  2. Jakie są zalety i wady a) tworzenia globalnego kontekstu encji dla aplikacji (tj. Jednej instancji statycznej) lub b) tworzenia i ujawniania kontekstu encji dla każdej danej operacji / metody, z usingblokiem.

  3. Wszelkie inne zalecenia, najlepsze praktyki lub wspólne podejścia do niektórych scenariuszy, o których powinienem wiedzieć?

Noldorin
źródło

Odpowiedzi:

369
  1. Pula połączeń jest obsługiwana jak w każdej innej aplikacji ADO.NET. Połączenie encji nadal korzysta z tradycyjnego połączenia z bazą danych z tradycyjnym ciągiem połączenia. Wierzę, że możesz wyłączyć pulowanie połączeń w ciągu połączenia, jeśli nie chcesz go używać. (czytaj więcej o SQL Server Connection Pooling (ADO.NET) )
  2. Nigdy nie używaj globalnego kontekstu. ObjectContext wewnętrznie implementuje kilka wzorców, w tym mapę tożsamości i jednostkę pracy. Wpływ korzystania z kontekstu globalnego jest różny w zależności od typu aplikacji.
  3. W przypadku aplikacji internetowych użyj pojedynczego kontekstu na żądanie. W przypadku usług internetowych użyj jednego kontekstu na połączenie. W aplikacji WinForms lub WPF używaj pojedynczego kontekstu na formularz lub na prezentera. Mogą istnieć pewne specjalne wymagania, które nie pozwolą na zastosowanie tego podejścia, ale w większości przypadków to wystarczy.

Jeśli chcesz wiedzieć, jaki wpływ ma kontekst jednego obiektu dla aplikacji WPF / WinForm, sprawdź ten artykuł . Chodzi o sesję NHibernate, ale pomysł jest taki sam.

Edytować:

Gdy używasz EF, domyślnie ładuje każdą jednostkę tylko raz na kontekst. Pierwsze zapytanie tworzy instancję encji i przechowuje ją wewnętrznie. Każde kolejne zapytanie, które wymaga encji o tym samym kluczu, zwraca tę przechowywaną instancję. Jeśli wartości w magazynie danych ulegną zmianie, nadal otrzymasz encję z wartościami z początkowego zapytania. Nazywa się to wzorem mapy tożsamości . Możesz zmusić kontekst obiektu do przeładowania bytu, ale przeładuje on jedną udostępnioną instancję.

Wszelkie zmiany dokonane w encji nie są utrwalane, dopóki nie zadzwonisz SaveChangesw kontekście. Możesz wprowadzać zmiany w wielu jednostkach i przechowywać je jednocześnie. Jest to tak zwany wzorzec jednostki pracy . Nie możesz selektywnie powiedzieć, który zmodyfikowany dołączony element chcesz zapisać.

Połącz te dwa wzory, a zobaczysz kilka interesujących efektów. Masz tylko jedną instancję encji dla całej aplikacji. Wszelkie zmiany encji wpływają na całą aplikację, nawet jeśli zmiany nie zostały jeszcze utrwalone (zatwierdzone). W większości przypadków nie tego chcesz. Załóżmy, że masz formularz edycji w aplikacji WPF. Pracujesz z jednostką i decydujesz się anulować złożoną edycję (zmienianie wartości, dodawanie powiązanych jednostek, usuwanie innych powiązanych jednostek itp.). Ale jednostka jest już zmodyfikowana w kontekście współdzielonym. Co zrobisz? Wskazówka: Nie wiem o żadnych zmianach Anuluj lub Cofnij zmiany ObjectContext.

Myślę, że nie musimy omawiać scenariusza serwera. Po prostu współużytkowanie jednego elementu między wieloma żądaniami HTTP lub połączeniami z usługami sieci Web sprawia, że ​​aplikacja nie jest potrzebna. Każde żądanie może po prostu uruchomić SaveChangesi zapisać częściowe dane z innego żądania, ponieważ dzielisz jedną jednostkę pracy między nimi wszystkimi. Będzie to również miało inny problem - kontekst i wszelkie manipulacje z obiektami w kontekście lub połączenie z bazą danych używane przez kontekst nie są bezpieczne dla wątków.

Nawet w przypadku aplikacji przeznaczonej tylko do odczytu kontekst globalny nie jest dobrym wyborem, ponieważ zapewne potrzebne są świeże dane przy każdym zapytaniu do aplikacji.

Ladislav Mrnka
źródło
Dzięki za odpowiedź. Być może mógłbyś wyjaśnić, dlaczego stosowanie jednego globalnego kontekstu jest złe? Z pewnością utrudnia to dostęp równoległy, ale co jeszcze ...?
Noldorin,
Ok, teraz jest o wiele jaśniej, dziękuję. Tylko po to, aby potwierdzić, chociaż kontekst globalny nigdy nie jest tak naprawdę odpowiedni, pojedynczy kontekst dla „okna edycji” lub taki może być właściwy sposób? W innych sytuacjach, takich jak usługi sieciowe i ASP.NET, konteksty w ramach metod mają tylko większy sens. O poprawne?
Noldorin,
Wziąłem twoją radę i usunąłem singelton. Teraz pojawia się kolejny błąd: stackoverflow.com/questions/14795899/...
Elad Benda
Rozumiem, że wdrożenie wzorca Jednostki pracy i enkapsulacja DbContext powinny oddzielić logikę biznesową i operacje na bazach danych. Nie jestem w stanie zrozumieć, jak zaimplementować wzorzec jednostki pracy i używać TransactionScope tylko do niektórych operacji.
Rudolf Dvoracek
4
@RudolfDvoracek: Łatwo. TransactionScopenie należy do jednostki pracy, należy do logiki biznesowej, ponieważ sama logika definiuje transakcję. Jednostka pracy określa tylko to, co powinno być utrwalone razem, podczas gdy zakres transakcji pozwala na wielokrotne użycie trwałości jednostki pracy w ramach tej samej transakcji.
Ladislav Mrnka
70

Według Daniela Simmonsa:

Utwórz nową instancję ObjectContext w instrukcji Using dla każdej metody usługi, aby była usuwana przed powrotem metody. Ten krok ma kluczowe znaczenie dla skalowalności usługi. Zapewnia to, że połączenia z bazą danych nie są utrzymywane otwarte w ramach zgłoszeń serwisowych, a stan tymczasowy używany przez określoną operację jest usuwany po wyrzuceniu elementów bezużytecznych. Entity Framework automatycznie buforuje metadane i inne informacje, których potrzebuje w domenie aplikacji, a ADO.NET łączy bazy danych, więc ponowne tworzenie kontekstu za każdym razem jest szybką operacją.

To jest z jego obszernego artykułu tutaj:

http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

Uważam, że ta rada dotyczy żądań HTTP, więc byłaby ważna dla ASP.NET. Stanowa aplikacja typu gruby klient, taka jak aplikacja WPF, może być jedynym przypadkiem w kontekście „współużytkowanym”.

Dave Swersky
źródło
Dzięki, jest to bardzo pouczający cytat. Nadal jednak zastanawiam się, czy współużytkowany (globalny) kontekst byłby odpowiedni nawet dla aplikacji klienta WPF lub podobnej. Czy nawet w tym przypadku jest jakaś korzyść?
Noldorin,
Kontekst globalny nie miałby żadnej korzyści w aplikacji WPF, ale prawdopodobnie nie byłoby też znaczącej szkody. W przypadku implementacji kontekstu globalnego konieczne może być ręczne zarządzanie połączeniami z bazą danych (jawne zamknięcie połączenia) w przypadku wysokich wskaźników żądań.
Dave Swersky,
1
Dobrze; więc zasadniczo nigdy nie mogę się pomylić, używając wielu tymczasowych kontekstów (biorąc pod uwagę, że dzieje się pula połączeń)? ... Jeśli korzystasz z jednego globalnego kontekstu, czy teoretycznie połączenie nie może spaść w przypadkowym momencie?
Noldorin,
1
@Nolodrin: Nie sądzę, aby połączenie spadło „losowo” ... istnieje ryzyko, że połączenia będą zbyt długo otwarte i nasycą pulę połączeń.
Dave Swersky,
1
W moim przekonaniu narzędzie ObjectContext / DbContext IDisposablepowinno być otwarte przez możliwie najkrótszy czas.
nicodemus13
12

Zgodnie z dokumentacją EF6 (również 4,5): https://msdn.microsoft.com/en-us/data/hh949853#9

9.3 Kontekst na żądanie

Konteksty Entity Framework mają być używane jako instancje krótkotrwałe w celu zapewnienia najbardziej optymalnej wydajności . Oczekuje się, że konteksty będą krótkotrwałe i odrzucone, i jako takie zostały wdrożone jako bardzo lekkie i ponownie wykorzystują metadane, gdy tylko jest to możliwe. W scenariuszach sieciowych ważne jest, aby o tym pamiętać i nie mieć kontekstu dłużej niż czas trwania pojedynczego żądania. Podobnie w scenariuszach innych niż sieci kontekst powinien zostać odrzucony w oparciu o Twoją wiedzę na temat różnych poziomów buforowania w Entity Framework. Ogólnie rzecz biorąc, należy unikać wystąpienia kontekstu przez cały okres użytkowania aplikacji, a także kontekstów na wątek i kontekstów statycznych.

Raj Rao
źródło
2
Wiem, że ta odpowiedź już tu była, ale muszę powiedzieć, że zaoszczędziło mi to mnóstwo bólu głowy. Utrzymywanie błędu „Połączenie puli” podczas korzystania z EF z Oracle i nie mogłem zrozumieć, dlaczego. Ustawiłem dbContext jako zmienną klasową, tworząc ją podczas tworzenia. Zmieniając go na tworzenie kontekstu w miarę potrzeb, naprawiłem wszystkie bolączki mojego świata. Dziękuję Ci!
Fletchius
1

Poniższy kod pomógł mojemu obiektowi odświeżyć się świeżymi wartościami w bazie danych. Komenda Entry (object) .Reload () zmusza obiekt do przywołania wartości z bazy danych

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
HGMamaci
źródło
a także w przypadku kolekcji (kod VB):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Ivan Ferrer Villa