Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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łączeniami i transakcjami. |
| Generowanie widoków | Wysoki | 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 na kolekcjach 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 opcji AppendOnly, PreserveChanges lub OverwriteChanges scalania, wyniki zapytania są śledzone w 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 dla istniejącego ObjectStateEntry można znaleźć EntityKey, zwracany jest ten istniejący obiekt. Jeśli używana jest opcja PreserveChanges lub OverwriteChanges, obiekt zostanie zaktualizowany przed jego zwróceniem. Aby uzyskać więcej informacji, zobacz Rozwiązywanie tożsamości, Zarządzanie stanem i Śledzenie zmian. |
| 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 ObjectContext i zapytanie używa opcji łączenia AppendOnly lub PreserveChanges, ten etap nie ma wpływu na wydajność. Aby uzyskać więcej informacji, zobacz Rozwiązywanie tożsamości, Zarządzanie stanem i Śledzenie zmian. |
1 Gdy dostawca źródła danych implementuje pulowanie połączeń, koszt otwarcia połączenia jest rozłożony 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 zapytania ObjectQuery<T> lub zapytania 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 metody Execute na ObjectQuery<T> lub podczas wywoływania metody LINQ zwracającej zapytanie pojedynczego obiektu, 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 1:1 między jednostkami w modelu koncepcyjnym a tabelami w modelu przechowywania, generują bardziej złożone polecenia niż modele, które mają mapowanie 1:1.
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 / Notatka
Użyj metody klasy ToTraceString lub ObjectQuery<T>, 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 cPonadto takie zapytania powodują, że potok zapytań generuje pojedyncze zapytanie, powodującą 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 Instrukcje: Stronicowanie 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 do funkcji tabelarycznej lub procedury składowanej.
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>, powiązane obiekty nie są zwracane (chociaż obiekty reprezentujące same relacje są). Obiekty pokrewne można załadować na jeden z trzech sposobów:
Ustaw ścieżkę zapytania przed wykonaniem ObjectQuery<T> .
Wywołaj metodę
Loadwe właściwości nawigacji, którą udostępnia obiekt.Ustaw opcję LazyLoadingEnabled na ObjectContext wartość
true. Należy pamiętać, że jest to wykonywane automatycznie podczas generowania kodu warstwy obiektu za pomocą projektanta 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ż jedno lub więcej łączeń jest wymagane do zwrócenia powiązanych ze sobą 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 / Notatka
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 metodę Load na właściwości nawigacyjnej zwracającej EntityCollection<TEntity> lub EntityReference<TEntity>. Jawne ładowanie obiektów wymaga dodatkowej podróży do bazy danych za każdym razem, gdy Load jest wywoływane.
Uwaga / Notatka
Jeśli wywołujesz Load podczas pętli przez kolekcję zwracanych obiektów, tak jak w przypadku używania instrukcji foreach (For Each w Visual Basic), dostawca 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 parametrach połączenia dostawcy.
Można również użyć metody LoadProperty, jeśli jednostki nie mają właściwości EntityCollection<TEntity> ani EntityReference<TEntity>. 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
Gdy wywołasz metodę SaveChanges na obiekcie ObjectContext, zostanie wygenerowane oddzielne polecenie tworzenia, aktualizacji lub usuwania dla każdego dodanego, zaktualizowanego lub usuniętego obiektu w kontekście. Te instrukcje są wykonywane na ź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, które wymagają zasobów zarządzanych przez koordynatora transakcji rozproszonej (DTC), będą znacznie droższe niż podobne operacje, które nie wymagają DTC. Podwyższenie poziomu do usługi DTC nastąpi w następujących sytuacjach:
Transakcja jawna dotycząca operacji na bazie danych SQL Server 2000 lub innym źródle danych, która zawsze eskaluje transakcje jawne do koordynatora 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łączeniami i transakcjami.
Transakcja jawna jest stosowana, gdy wewnątrz transakcji wykonywana jest co najmniej jedna operacja System.Transactions. Aby uzyskać więcej informacji, zobacz Zarządzanie połączeniami 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ń
Śledzenie zwracanych obiektów w kontekście obiektów wiąże się z kosztami. Wykrywanie zmian w obiektach oraz zapewnienie, że wiele żądań dotyczących tej samej jednostki logicznej zwraca to samo wystąpienie obiektu, wymaga, aby obiekty były dołączone do wystąpienia ObjectContext. 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 Instrukcje: Stronicowanie wyników zapytania.
Ogranicz zakres ObjectContext
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 kontrolki są powiązane z obiektami zarządzanymi przez kontekst obiektu, należy utrzymywać wystąpienie ObjectContext tak długo, jak jest potrzebne, a następnie ręcznie je usunąć. Aby uzyskać więcej informacji, zobacz Zarządzanie połączeniami 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łączeniami 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: