Zagadnienia dotyczące wydajności (Entity Framework)
W tym temacie opisano charakterystykę wydajności ADO.NET Entity Framework i przedstawiono kilka zagadnień, które pomogą zwiększyć wydajność aplikacji platformy Entity Framework.
Etapy wykonywania zapytań
Aby lepiej zrozumieć wydajność zapytań w programie Entity Framework, warto zrozumieć operacje wykonywane podczas wykonywania zapytania względem modelu koncepcyjnego i zwracania danych jako obiektów. W poniższej tabeli opisano tę serię operacji.
Operacja | Koszt względny | Częstotliwość | Komentarze |
---|---|---|---|
Ładowanie metadanych | Umiarkowane | Raz w każdej domenie aplikacji. | Metadane modelu i mapowania używane przez program Entity Framework są ładowane do elementu MetadataWorkspace. Te metadane są buforowane globalnie i są dostępne dla innych wystąpień ObjectContext w tej samej domenie aplikacji. |
Otwieranie połączenia z bazą danych | Umiarkowany1 | W razie potrzeby. | Ponieważ otwarte połączenie z bazą danych zużywa cenny zasób, program Entity Framework otwiera i zamyka połączenie z bazą danych tylko zgodnie z potrzebami. Możesz również jawnie otworzyć połączenie. Aby uzyskać więcej informacji, zobacz Zarządzanie Połączenie ionami i transakcjami. |
Generowanie widoków | Wys. | Raz w każdej domenie aplikacji. (Można wstępnie wygenerować). | Zanim program Entity Framework będzie mógł wykonać zapytanie względem modelu koncepcyjnego lub zapisać zmiany w źródle danych, musi wygenerować zestaw lokalnych widoków zapytań w celu uzyskania dostępu do bazy danych. Ze względu na wysokie koszty generowania tych widoków można wstępnie wygenerować widoki i dodać je do projektu w czasie projektowania. Aby uzyskać więcej informacji, zobacz How to: Pre-Generate Views to Improve Query Performance (Instrukcje: wstępne generowanie widoków w celu zwiększenia wydajności zapytań). |
Przygotowywanie zapytania | Umiarkowany2 | Raz dla każdego unikatowego zapytania. | Obejmuje koszty tworzenia polecenia zapytania, generowania drzewa poleceń na podstawie metadanych modelu i mapowania oraz definiowania kształtu zwracanych danych. Ponieważ teraz zarówno polecenia zapytania Entity SQL, jak i zapytania LINQ są buforowane, późniejsze wykonania tego samego zapytania zajmują mniej czasu. Nadal można użyć skompilowanych zapytań LINQ, aby zmniejszyć ten koszt w kolejnych wykonaniach i skompilowane zapytania mogą być bardziej wydajne niż zapytania LINQ, które są automatycznie buforowane. Aby uzyskać więcej informacji, zobacz Skompilowane zapytania (LINQ to Entities). Aby uzyskać ogólne informacje na temat wykonywania zapytań LINQ, zobacz LINQ to Entities (Jednostki LINQ to Entities). Uwaga: zapytania LINQ to Entities, które stosują Enumerable.Contains operator do kolekcji w pamięci, nie są automatycznie buforowane. Ponadto parametryzacja kolekcji w pamięci w skompilowanych zapytaniach LINQ nie jest dozwolona. |
Wykonywanie zapytania | Niski2 | Raz dla każdego zapytania. | Koszt wykonywania polecenia względem źródła danych przy użyciu dostawcy danych ADO.NET. Ponieważ większość źródeł danych buforuje plany zapytań, późniejsze wykonania tego samego zapytania mogą zająć jeszcze mniej czasu. |
Ładowanie i weryfikowanie typów | Niski3 | Raz dla każdego ObjectContext wystąpienia. | Typy są ładowane i weryfikowane względem typów, które definiuje model koncepcyjny. |
Śledzenie | Niski3 | Raz dla każdego obiektu zwracanego przez zapytanie. 4 | Jeśli zapytanie używa NoTracking opcji scalania, ten etap nie ma wpływu na wydajność. Jeśli zapytanie używa AppendOnlyopcji , PreserveChangeslub OverwriteChanges scalania, wyniki zapytania są śledzone w pliku ObjectStateManager. Element EntityKey jest generowany dla każdego śledzonego obiektu, który zwraca zapytanie i jest używany do utworzenia obiektu ObjectStateEntry w obiekcie ObjectStateManager. Jeśli istnieje ObjectStateEntry EntityKeydla obiektu , zwracany jest istniejący obiekt. PreserveChangesJeśli jest używana opcja lub OverwriteChanges , obiekt zostanie zaktualizowany przed jego zwróceniem. Aby uzyskać więcej informacji, zobacz Identity Resolution, State Management i Change Tracking. |
Materializowanie obiektów | Umiarkowany3 | Raz dla każdego obiektu zwracanego przez zapytanie. 4 | Proces odczytywania zwróconego DbDataReader obiektu oraz tworzenia obiektów i ustawiania wartości właściwości opartych na wartościach w każdym wystąpieniu DbDataRecord klasy. Jeśli obiekt już istnieje w obiekcie ObjectContext , a zapytanie używa AppendOnly opcji lub PreserveChanges scalania, ten etap nie ma wpływu na wydajność. Aby uzyskać więcej informacji, zobacz Identity Resolution, State Management i Change Tracking. |
1 Gdy dostawca źródła danych implementuje buforowanie połączeń, koszt otwarcia połączenia jest dystrybuowany w całej puli. Dostawca .NET dla programu SQL Server obsługuje buforowanie połączeń.
2 Koszty zwiększają się wraz ze zwiększoną złożonością zapytań.
3 Całkowity koszt zwiększa się proporcjonalnie do liczby obiektów zwracanych przez zapytanie.
4 To obciążenie nie jest wymagane w przypadku zapytań EntityClient, ponieważ zapytania EntityClient zwracają EntityDataReader zamiast obiektów. Aby uzyskać więcej informacji, zobacz EntityClient Provider for the Entity Framework (Dostawca EntityClient dla programu Entity Framework).
Dodatkowe zagadnienia
Poniżej przedstawiono inne zagadnienia, które mogą mieć wpływ na wydajność aplikacji platformy Entity Framework.
Wykonywanie zapytania
Ponieważ zapytania mogą intensywnie obciążać zasoby, należy wziąć pod uwagę, w jakim momencie w kodzie i na jakim komputerze jest wykonywane zapytanie.
Odroczone i natychmiastowe wykonanie
Podczas tworzenia ObjectQuery<T> zapytania LUB LINQ zapytanie może nie być wykonywane natychmiast. Wykonanie zapytania jest odroczone do momentu, gdy będą potrzebne wyniki, takie jak wyliczenie foreach
(C#) lub For Each
(Visual Basic) lub gdy zostanie przypisane do wypełnienia List<T> kolekcji. Wykonywanie zapytania rozpoczyna się natychmiast po wywołaniu Execute metody w metodzie ObjectQuery<T> lub podczas wywoływania metody LINQ zwracającej jednotonowe zapytanie, takie jak First lub Any. Aby uzyskać więcej informacji, zobacz Zapytania obiektów i wykonywanie zapytań (LINQ to Entities).
Wykonywanie zapytań LINQ po stronie klienta
Mimo że wykonywanie zapytania LINQ odbywa się na komputerze hostujący źródło danych, niektóre części zapytania LINQ mogą być oceniane na komputerze klienckim. Aby uzyskać więcej informacji, zobacz sekcję Przechowywanie wykonywania zapytań (LINQ to Entities).
Złożoność zapytań i mapowania
Złożoność poszczególnych zapytań i mapowania w modelu jednostki będzie miała znaczący wpływ na wydajność zapytań.
Złożoność mapowania
Modele, które są bardziej złożone niż proste mapowanie jeden do jednego między jednostkami w modelu koncepcyjnym i tabelach w modelu magazynu, generują bardziej złożone polecenia niż modele, które mają mapowanie jeden do jednego.
Złożoność zapytań
Zapytania wymagające dużej liczby sprzężeń w poleceniach wykonywanych względem źródła danych lub zwracające dużą ilość danych mogą mieć wpływ na wydajność w następujący sposób:
Zapytania względem modelu koncepcyjnego, które wydają się proste, mogą spowodować wykonanie bardziej złożonych zapytań względem źródła danych. Może się tak zdarzyć, ponieważ platforma Entity Framework tłumaczy zapytanie względem modelu koncepcyjnego na równoważne zapytanie względem źródła danych. Jeśli jedna jednostka ustawiona w modelu koncepcyjnym jest mapowana na więcej niż jedną tabelę w źródle danych lub gdy relacja między jednostkami jest mapowana na tabelę sprzężenia, polecenie zapytania wykonywane względem zapytania źródła danych może wymagać co najmniej jednego sprzężenia.
Uwaga
ToTraceString Użyj metody ObjectQuery<T> klasy lubEntityCommand, aby wyświetlić polecenia wykonywane względem źródła danych dla danego zapytania. Aby uzyskać więcej informacji, zobacz How to: View the Store Commands (Instrukcje: wyświetlanie poleceń sklepu).
Zagnieżdżone zapytania SQL jednostek mogą tworzyć sprzężenia na serwerze i zwracać dużą liczbę wierszy.
Poniżej przedstawiono przykład zagnieżdżonego zapytania w klauzuli projekcji:
SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c ) As Inner2 FROM AdventureWorksModel.JobCandidate AS c ) As Inner1 FROM AdventureWorksModel.EmployeeDepartmentHistory AS c
Ponadto takie zapytania powodują wygenerowanie pojedynczego zapytania z duplikacją obiektów w zagnieżdżonych zapytaniach. W związku z tym pojedyncza kolumna może być duplikowana wiele razy. W niektórych bazach danych, w tym w programie SQL Server, może to spowodować, że tabela TempDB będzie bardzo duża, co może zmniejszyć wydajność serwera. Podczas wykonywania zagnieżdżonych zapytań należy zachować ostrożność.
Wszystkie zapytania zwracające dużą ilość danych mogą spowodować zmniejszenie wydajności, jeśli klient wykonuje operacje, które zużywają zasoby w sposób proporcjonalny do rozmiaru zestawu wyników. W takich przypadkach należy rozważyć ograniczenie ilości danych zwracanych przez zapytanie. Aby uzyskać więcej informacji, zobacz How to: Page Through Query Results (Instrukcje: stronicowanie za pośrednictwem wyników zapytania).
Wszystkie polecenia generowane automatycznie przez program Entity Framework mogą być bardziej złożone niż podobne polecenia napisane jawnie przez dewelopera bazy danych. Jeśli potrzebujesz jawnej kontroli nad poleceniami wykonywanymi względem źródła danych, rozważ zdefiniowanie mapowania na funkcję wartości tabeli lub procedurę składowaną.
Relacje
Aby uzyskać optymalną wydajność zapytań, należy zdefiniować relacje między jednostkami zarówno jako skojarzenia w modelu jednostki, jak i jako relacje logiczne w źródle danych.
Ścieżki zapytania
Domyślnie podczas wykonywania ObjectQuery<T>obiektu powiązane obiekty nie są zwracane (chociaż obiekty reprezentujące same relacje). Obiekty pokrewne można załadować na jeden z trzech sposobów:
Ustaw ścieżkę zapytania przed wykonaniem ObjectQuery<T> .
Wywołaj metodę
Load
we właściwości nawigacji uwidacznianej przez obiekt.LazyLoadingEnabled Ustaw opcję na ObjectContext wartość
true
. Należy pamiętać, że jest to wykonywane automatycznie podczas generowania kodu warstwy obiektu za pomocą Projektant modelu danych jednostki. Aby uzyskać więcej informacji, zobacz Wygenerowany kod — omówienie.
Podczas rozważania, która opcja ma być używana, należy pamiętać, że istnieje kompromis między liczbą żądań względem bazy danych a ilością danych zwracanych w jednym zapytaniu. Aby uzyskać więcej informacji, zobacz Ładowanie powiązanych obiektów.
Używanie ścieżek zapytania
Ścieżki zapytań definiują graf obiektów zwracanych przez zapytanie. Podczas definiowania ścieżki zapytania wymagane jest tylko jedno żądanie względem bazy danych w celu zwrócenia wszystkich obiektów zdefiniowanych przez ścieżkę. Używanie ścieżek zapytań może spowodować wykonywanie złożonych poleceń względem źródła danych z pozornie prostych zapytań dotyczących obiektów. Dzieje się tak, ponieważ co najmniej jedno sprzężenie jest wymagane do zwrócenia powiązanych obiektów w jednym zapytaniu. Ta złożoność jest większa w zapytaniach względem złożonego modelu jednostki, na przykład jednostki z dziedziczeniem lub ścieżką zawierającą relacje wiele-do-wielu.
Uwaga
ToTraceString Użyj metody , aby wyświetlić polecenie, które zostanie wygenerowane przez element ObjectQuery<T>. Aby uzyskać więcej informacji, zobacz How to: View the Store Commands (Instrukcje: wyświetlanie poleceń sklepu).
Jeśli ścieżka zapytania zawiera zbyt wiele powiązanych obiektów lub obiekty zawierają zbyt dużo danych wierszy, źródło danych może nie być w stanie ukończyć zapytania. Dzieje się tak, jeśli zapytanie wymaga pośredniego magazynu tymczasowego, który przekracza możliwości źródła danych. W takim przypadku można zmniejszyć złożoność zapytania źródła danych, jawnie ładując powiązane obiekty.
Jawne ładowanie powiązanych obiektów
Obiekty pokrewne można jawnie załadować, wywołując Load
metodę we właściwości nawigacji zwracającej EntityCollection<TEntity> obiekt lub EntityReference<TEntity>. Jawne ładowanie obiektów wymaga zaokrąglonej podróży do bazy danych za każdym razem, gdy Load
jest wywoływana.
Uwaga
Jeśli wywołujesz Load
wywołanie podczas pętli przez kolekcję zwracanych obiektów, takich jak w przypadku używania foreach
instrukcji (For Each
w Visual Basic), dostawca specyficzny dla źródła danych musi obsługiwać wiele aktywnych zestawów wyników w jednym połączeniu. W przypadku bazy danych programu SQL Server należy określić wartość MultipleActiveResultSets = true
w parametry połączenia dostawcy.
Można również użyć LoadProperty metody , jeśli nie EntityCollection<TEntity> ma właściwości lub EntityReference<TEntity> w jednostkach. Jest to przydatne w przypadku korzystania z jednostek POCO.
Mimo że jawne ładowanie powiązanych obiektów zmniejszy liczbę sprzężeń i zmniejszy ilość nadmiarowych danych, Load
wymaga powtarzających się połączeń z bazą danych, co może stać się kosztowne podczas jawnego ładowania dużej liczby obiektów.
Zapisywanie zmian
Podczas wywoływania SaveChanges metody w ObjectContextobiekcie jest generowane oddzielne polecenie tworzenia, aktualizacji lub usuwania dla każdego dodanego, zaktualizowanego lub usuniętego obiektu w kontekście. Te polecenia są wykonywane w źródle danych w jednej transakcji. Podobnie jak w przypadku zapytań, wydajność operacji tworzenia, aktualizowania i usuwania zależy od złożoności mapowania w modelu koncepcyjnym.
Transakcje rozproszone
Operacje w transakcji jawnej wymagającej zasobów zarządzanych przez koordynatora transakcji rozproszonej (DTC) będą znacznie droższe niż podobna operacja, która nie wymaga dtC. Podwyższenie poziomu do usługi DTC nastąpi w następujących sytuacjach:
Jawna transakcja z operacją względem bazy danych programu SQL Server 2000 lub innego źródła danych, które zawsze podwyższają jawne transakcje do usługi DTC.
Jawna transakcja z operacją względem programu SQL Server 2005, gdy połączenie jest zarządzane przez program Entity Framework. Dzieje się tak, ponieważ program SQL Server 2005 promuje usługę DTC za każdym razem, gdy połączenie zostanie zamknięte i ponownie otwarte w ramach jednej transakcji, co jest domyślnym zachowaniem programu Entity Framework. Ta promocja DTC nie występuje w przypadku korzystania z programu SQL Server 2008. Aby uniknąć tej promocji podczas korzystania z programu SQL Server 2005, należy jawnie otworzyć i zamknąć połączenie w ramach transakcji. Aby uzyskać więcej informacji, zobacz Zarządzanie Połączenie ionami i transakcjami.
Jawna transakcja jest używana, gdy co najmniej jedna operacja jest wykonywana System.Transactions wewnątrz transakcji. Aby uzyskać więcej informacji, zobacz Zarządzanie Połączenie ionami i transakcjami.
Strategie poprawy wydajności
Ogólną wydajność zapytań w programie Entity Framework można poprawić, korzystając z poniższych strategii.
Wstępne generowanie widoków
Generowanie widoków na podstawie modelu jednostki jest znaczącym kosztem przy pierwszym wykonaniu zapytania przez aplikację. Użyj narzędzia EdmGen.exe, aby wstępnie wygenerować widoki jako plik kodu języka Visual Basic lub C#, który można dodać do projektu podczas projektowania. Można również użyć zestawu narzędzi do przekształcania szablonów tekstu do generowania wstępnie skompilowanych widoków. Wstępnie wygenerowane widoki są weryfikowane w czasie wykonywania, aby upewnić się, że są one zgodne z bieżącą wersją określonego modelu jednostki. Aby uzyskać więcej informacji, zobacz How to: Pre-Generate Views to Improve Query Performance (Instrukcje: wstępne generowanie widoków w celu zwiększenia wydajności zapytań).
Podczas pracy z bardzo dużymi modelami należy wziąć pod uwagę następujące kwestie:
Format metadanych platformy .NET ogranicza liczbę znaków ciągu użytkownika w danym pliku binarnym do 16 777 215 (0xFFFFFF). Jeśli generujesz widoki dla bardzo dużego modelu, a plik widoku osiągnie ten limit rozmiaru, zostanie wyświetlony błąd kompilowania "Brak miejsca logicznego do utworzenia większej liczby ciągów użytkownika". To ograniczenie rozmiaru dotyczy wszystkich zarządzanych plików binarnych. Aby uzyskać więcej informacji, zobacz blog , który pokazuje, jak uniknąć błędu podczas pracy z dużymi i złożonymi modelami.
Rozważ użycie opcji scalania NoTracking dla zapytań
Istnieje koszt wymagany do śledzenia zwracanych obiektów w kontekście obiektu. Wykrywanie zmian w obiektach i upewnienie się, że wiele żądań dla tej samej jednostki logicznej zwraca to samo wystąpienie obiektu, wymaga dołączenia obiektów do ObjectContext wystąpienia. Jeśli nie planujesz aktualizowania lub usuwania obiektów i nie wymagasz zarządzania tożsamościami, rozważ użycie NoTracking opcji scalania podczas wykonywania zapytań.
Zwracanie prawidłowej ilości danych
W niektórych scenariuszach określenie ścieżki zapytania przy użyciu Include metody jest znacznie szybsze, ponieważ wymaga mniejszej liczby rund do bazy danych. Jednak w innych scenariuszach dodatkowe rundy do bazy danych w celu załadowania powiązanych obiektów mogą być szybsze, ponieważ prostsze zapytania z mniejszą liczbą sprzężeń powodują mniejszą nadmiarowość danych. W związku z tym zalecamy przetestowanie wydajności różnych sposobów pobierania powiązanych obiektów. Aby uzyskać więcej informacji, zobacz Ładowanie powiązanych obiektów.
Aby uniknąć zwracania zbyt dużej ilości danych w jednym zapytaniu, rozważ stronicowanie wyników zapytania w bardziej zarządzane grupy. Aby uzyskać więcej informacji, zobacz How to: Page Through Query Results (Instrukcje: stronicowanie za pośrednictwem wyników zapytania).
Ograniczanie zakresu obiektuContext
W większości przypadków należy utworzyć ObjectContext wystąpienie w instrukcji using
(Using…End Using
w Visual Basic). Może to zwiększyć wydajność, upewniając się, że zasoby skojarzone z kontekstem obiektu są usuwane automatycznie, gdy kod zamyka blok instrukcji. Jeśli jednak kontrolki są powiązane z obiektami zarządzanymi przez kontekst obiektu, ObjectContext wystąpienie powinno być utrzymywane tak długo, jak jest potrzebne, i usuwane ręcznie. Aby uzyskać więcej informacji, zobacz Zarządzanie Połączenie ionami i transakcjami.
Rozważ ręczne otwarcie połączenia z bazą danych
Gdy aplikacja wykonuje serię zapytań dotyczących obiektów lub często wywołuje SaveChanges operacje tworzenia, aktualizowania i usuwania w źródle danych, program Entity Framework musi stale otwierać i zamykać połączenie ze źródłem danych. W takich sytuacjach należy rozważyć ręczne otwarcie połączenia na początku tych operacji i zamknięcie lub usunięcie połączenia po zakończeniu operacji. Aby uzyskać więcej informacji, zobacz Zarządzanie Połączenie ionami i transakcjami.
Dane wydajności
Niektóre dane dotyczące wydajności programu Entity Framework są publikowane w następujących wpisach na blogu zespołu ADO.NET: