Korzystam z Entity Framework i od czasu do czasu dostaję ten błąd.
EntityCommandExecutionException
{"There is already an open DataReader associated with this Command which must be closed first."}
at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands...
Mimo że nie wykonuję żadnego ręcznego zarządzania połączeniami.
ten błąd zdarza się sporadycznie.
kod wywołujący błąd (skrócony dla ułatwienia odczytu):
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
za pomocą opcji Usuń wzór, aby za każdym razem otwierać nowe połączenie.
using (_tEntitites = new TEntities(GetEntityConnection())) {
if (critera.FromDate > x) {
t= _tEntitites.T.Where(predicate).ToList();
}
else {
t= new List<T>(_tEntitites.TA.Where(historicPredicate).ToList());
}
}
wciąż problematyczne
dlaczego EF nie używałby połączenia, jeśli jest już otwarte.
linq
entity-framework
sql-server-2008
Sonic Soul
źródło
źródło
predicate
i jakiehistoricPredicate
zmienne. Odkryłem, że jeśli przejdzieszFunc<T, bool>
doWhere()
niego, to się skompiluje, a czasem zadziała (ponieważ robi to „gdzie” w pamięci). Co powinno się robić to przechodzącExpression<Func<T, bool>>
doWhere()
.Odpowiedzi:
Nie chodzi o zamykanie połączenia. EF poprawnie zarządza połączeniem. Rozumiem ten problem, ponieważ istnieje wiele poleceń pobierania danych wykonanych przy jednym połączeniu (lub pojedynczym poleceniu z wieloma zaznaczeniami), podczas gdy następny czytnik danych jest wykonywany przed zakończeniem odczytu. Jedynym sposobem uniknięcia tego wyjątku jest umożliwienie wielu zagnieżdżonych DataReaders = włączenie MultipleActiveResultSets. Innym scenariuszem, w którym zawsze tak się dzieje, jest iteracja przez wynik zapytania (IQueryable) i wyzwalanie leniwego ładowania dla załadowanej jednostki wewnątrz iteracji.
źródło
Alternatywnie do korzystania z MARS (MultipleActiveResultSets) możesz napisać kod, aby nie otwierać wielu zestawów wyników.
Możesz odzyskać dane do pamięci, w ten sposób nie będziesz mieć otwartego czytnika. Często jest to spowodowane iteracją zestawu wyników podczas próby otwarcia innego zestawu wyników.
Przykładowy kod:
Powiedzmy, że przeprowadzasz wyszukiwanie w bazie danych zawierającej:
Możemy to zrobić w prosty sposób, dodając .ToList () w następujący sposób:
To zmusza encjifrfrowej do załadowania listy do pamięci, dlatego gdy iterujemy ją w pętli foreach, nie korzysta już z czytnika danych do otwierania listy, zamiast tego jest w pamięci.
Zdaję sobie sprawę, że może to nie być pożądane, jeśli chcesz na przykład załadować niektóre właściwości. Jest to głównie przykład, który, mam nadzieję, wyjaśnia, w jaki sposób / dlaczego możesz dostać ten problem, abyś mógł odpowiednio podejmować decyzje
źródło
ToList
wymyślisz tysiąc obiektów, zwiększy to pamięć o tonę. W tym konkretnym przykładzie lepiej byłoby połączyć zapytanie wewnętrzne z pierwszym, aby wygenerować tylko jedno zapytanie, a nie dwa.Jest inny sposób na rozwiązanie tego problemu. To, czy jest to lepszy sposób, zależy od twojej sytuacji.
Problem wynika z leniwego ładowania, więc jednym ze sposobów uniknięcia tego jest brak leniwego ładowania, poprzez użycie opcji Dołącz:
Jeśli użyjesz odpowiednich
Include
liter, możesz uniknąć włączenia MARS. Ale jeśli go przegapisz, pojawi się błąd, więc włączenie MARS jest prawdopodobnie najłatwiejszym sposobem na jego naprawienie.źródło
.Include
jest znacznie lepszym rozwiązaniem niż włączenie MARS i znacznie łatwiejszym niż pisanie własnego kodu zapytania SQL.Ten błąd pojawia się, gdy kolekcja, którą próbujesz iterować, jest swego rodzaju leniwym ładowaniem (IQueriable).
Przekształcenie kolekcji IQueriable w inną kolekcję wymienną rozwiąże ten problem. przykład
Uwaga: .ToList () tworzy nowy zestaw za każdym razem i może powodować problemy z wydajnością w przypadku dużych danych.
źródło
SELECT COUNT(*) FROM Users
= 5Rozwiązałem problem łatwo (pragmatycznie), dodając opcję do konstruktora. Dlatego używam tego tylko w razie potrzeby.
źródło
Spróbuj ustawić parametry połączenia
MultipleActiveResultSets=true
. Umożliwia to wielozadaniowość w bazie danych.To działa dla mnie ... niezależnie od tego, czy połączenie w app.config, czy też ustawiasz je programowo ... mam nadzieję, że będzie to pomocne
źródło
Pierwotnie zdecydowałem się użyć pola statycznego w mojej klasie API, aby odwołać się do instancji obiektu MyDataContext (gdzie MyDataContext jest obiektem kontekstu EF5), ale to właśnie wydawało się stwarzać problem. Dodałem kod podobny do następującego do każdej z moich metod API i to rozwiązało problem.
Jak zauważyli inni, obiekty kontekstu danych EF NIE są bezpieczne dla wątków. Umieszczenie ich w obiekcie statycznym ostatecznie spowoduje błąd „czytnika danych” w odpowiednich warunkach.
Moje pierwotne założenie było takie, że utworzenie tylko jednej instancji obiektu byłoby bardziej wydajne i zapewniłoby lepsze zarządzanie pamięcią. Z tego, co zebrałem, badając ten problem, tak nie jest. W rzeczywistości wydaje się, że bardziej efektywne jest traktowanie każdego wywołania interfejsu API jako izolowanego, bezpiecznego dla wątków zdarzenia. Zapewnienie prawidłowego zwolnienia wszystkich zasobów, gdy obiekt wykracza poza zakres.
Ma to sens zwłaszcza, jeśli przejdziesz do następnego naturalnego postępu API, który polegałby na ujawnieniu go jako WebService lub REST API.
Ujawnienie
źródło
Zauważyłem, że ten błąd występuje, gdy wysyłam IQueriable do widoku i używam go w podwójnym foreach, gdzie wewnętrzny foreach również musi korzystać z połączenia. Prosty przykład (ViewBag.parents może być IQueriable lub DbSet):
Najprostszym rozwiązaniem jest użycie
.ToList()
tej kolekcji przed użyciem. Zauważ też, że MARS nie działa z MySQL.źródło
ToList()
swoje pierwsze połączenie, aby uzyskać kolekcję z bazy danych. Potem zrobiłem aforeach
na tej liście, a kolejne wywołania działały idealnie zamiast dawać błąd.Odkryłem, że mam ten sam błąd i wystąpił, gdy użyłem
Func<TEntity, bool>
zamiast zamiastExpression<Func<TEntity, bool>>
dla twojegopredicate
.Raz zmieniło się wszystko
Func's
, abyExpression's
wyjątkiem przestał wyrzucane.Wierzę, że
EntityFramwork
robi to pewne sprytne rzeczy, zExpression's
którymi po prostu nie ma nic wspólnegoFunc's
źródło
(MyTParent model, Func<MyTChildren, bool> func)
tak, aby moje ViewModels mogły określić określonąwhere
klauzulę dla ogólnej metody DataContext. Nic nie działało, dopóki tego nie zrobiłem.2 rozwiązania mające na celu złagodzenie tego problemu:
.ToList()
po zapytaniu, dzięki czemu możesz iterować przez niego otwierając nowy czytnik danych..Include
(/ dodatkowe encje, które chcesz załadować w zapytaniu /) nazywa się to chętnym ładowaniem, co pozwala (rzeczywiście) dołączyć powiązane obiekty (encje) podczas wykonywania zapytania za pomocą DataReadera.źródło
Dobrym pośrednikiem między włączeniem MARS a pobraniem całego zestawu wyników do pamięci jest pobranie tylko identyfikatorów w początkowym zapytaniu, a następnie przechodzenie przez identyfikatory materializujące każdą jednostkę w trakcie podróży.
Na przykład (używając przykładowych encji „Blog i posty” jak w tej odpowiedzi ):
Oznacza to, że wciąga się do pamięci tylko kilka tysięcy liczb całkowitych, w przeciwieństwie do tysięcy całych wykresów obiektowych, co powinno zminimalizować zużycie pamięci, umożliwiając jednocześnie pracę element po elemencie bez włączania MARS.
Inną zaletą tego, jak widać w przykładzie, jest to, że można zapisywać zmiany podczas przechodzenia przez każdy element, zamiast czekać na koniec pętli (lub inne tego rodzaju obejście), co byłoby potrzebne nawet przy MARS włączony (patrz tutaj i tutaj ).
źródło
context.SaveChanges();
inside loop :(. To nie jest dobre. musi być poza pętlą.W moim przypadku stwierdziłem, że przed wywołaniami myContext.SaveChangesAsync () brakowało instrukcji „czekaj”. Dodanie opcji oczekiwania przed tymi wywołaniami asynchronicznymi naprawiło dla mnie problemy z czytnikiem danych.
źródło
Jeśli spróbujemy zgrupować część naszych warunków w Func <> lub metodzie rozszerzenia, otrzymamy ten błąd, załóżmy, że mamy taki kod:
Spowoduje to wyrzucenie wyjątku, jeśli spróbujemy użyć go w Where (), zamiast tego powinniśmy zbudować Predicate:
Więcej informacji można znaleźć na stronie : http://www.albahari.com/nutshell/predicatebuilder.aspx
źródło
Problem ten można rozwiązać, po prostu przekształcając dane w listę
źródło
W mojej sytuacji problem wystąpił z powodu rejestracji wstrzyknięcia zależności. Wstrzykiwałem usługę zakresu na żądanie, która używała dbcontext do zarejestrowanej usługi singleton. W związku z tym kontekst db db został użyty w ramach wielu żądań i stąd błąd.
źródło
W moim przypadku problem nie miał nic wspólnego z ciągiem połączenia MARS, ale z serializacją json. Po uaktualnieniu mojego projektu z NetCore2 do 3 dostałem ten błąd.
Więcej informacji można znaleźć tutaj
źródło
Rozwiązałem ten problem, używając następującej sekcji kodu przed drugim zapytaniem:
możesz zmienić czas snu w milisekundach
PD Przydatne podczas używania wątków
źródło