Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wydajne wykonywanie zapytań to obszerny temat, który obejmuje tematy tak szeroko zakrojone jak indeksy, powiązane strategie ładowania jednostek i wiele innych. W tej sekcji opisano niektóre typowe zagadnienia dotyczące szybszego wykonywania zapytań oraz pułapki, na które użytkownicy zwykle napotykają.
Prawidłowe używanie indeksów
Głównym czynnikiem decydującym o tym, czy zapytanie działa szybko, czy nie, jest to, czy będzie prawidłowo korzystać z indeksów w odpowiednich przypadkach: bazy danych są zwykle używane do przechowywania dużych ilości danych i zapytań, które przechodzą przez całe tabele, są zwykle źródłami poważnych problemów z wydajnością. Problemy z indeksowaniem nie są łatwe do wykrycia, ponieważ nie jest od razu oczywiste, czy dana kwerenda będzie używać indeksu, czy nie. Przykład:
// Matches on start, so uses an index (on SQL Server)
var posts1 = await context.Posts.Where(p => p.Title.StartsWith("A")).ToListAsync();
// Matches on end, so does not use the index
var posts2 = await context.Posts.Where(p => p.Title.EndsWith("A")).ToListAsync();
Dobrym sposobem na wykrycie problemów z indeksowaniem jest najpierw wskazanie powolnego zapytania, a następnie sprawdzenie planu zapytania za pomocą ulubionego narzędzia bazy danych; Aby uzyskać więcej informacji na temat tego, jak to zrobić, zobacz stronę diagnostyki wydajności . Plan zapytania wyświetla, czy zapytanie przechodzi przez całą tabelę, czy używa indeksu.
Ogólnie rzecz biorąc, nie ma żadnej specjalnej wiedzy ef do korzystania z indeksów ani diagnozowania problemów z wydajnością związanych z nimi; ogólna wiedza na temat bazy danych związana z indeksami jest równie istotna dla aplikacji EF, jak w przypadku aplikacji, które nie korzystają z platformy EF. Poniżej wymieniono niektóre ogólne wytyczne, które należy wziąć pod uwagę podczas korzystania z indeksów:
- Podczas gdy indeksy przyspieszają zapytania, spowalniają one również aktualizacje, ponieważ muszą być przechowywane up-to-date. Unikaj definiowania indeksów, które nie są potrzebne, i rozważ użycie filtrów indeksu , aby ograniczyć indeks do podzbioru wierszy, zmniejszając tym samym obciążenie.
- Indeksy złożone mogą przyspieszyć zapytania, które filtrują wiele kolumn, ale mogą również przyspieszyć zapytania, które nie filtrują wszystkich kolumn indeksu — w zależności od kolejności. Na przykład indeks na kolumnach A i B przyspiesza zapytania filtrowane przez A i B, oraz te filtrowane tylko przez A, ale nie przyspiesza zapytań filtrowanych tylko po B.
- Jeśli zapytanie filtruje według wyrażenia w kolumnie (np.
price / 2
), nie można użyć prostego indeksu. Można jednak zdefiniować przechowywaną kolumnę utrwaloną dla wyrażenia i utworzyć indeks na niej. Niektóre bazy danych obsługują również indeksy wyrażeń, które mogą być używane bezpośrednio do przyspieszania filtrowania zapytań według dowolnego wyrażenia. - Różne bazy danych umożliwiają konfigurowanie indeksów na różne sposoby, a w wielu przypadkach dostawcy platformy EF Core udostępniają je za pośrednictwem interfejsu API Fluent. Na przykład dostawca programu SQL Server umożliwia skonfigurowanie, czy indeks jest klasterowany, czy też ustawiany jest jego współczynnik wypełnienia. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dostawcy.
Tylko potrzebne właściwości projektu
Program EF Core ułatwia wykonywanie zapytań dotyczących wystąpień jednostek, a następnie używanie tych wystąpień w kodzie. Jednak wykonywanie zapytań dotyczących wystąpień jednostek może często pobierać więcej danych niż jest to konieczne z bazy danych. Rozważ następujące kwestie:
await foreach (var blog in context.Blogs.AsAsyncEnumerable())
{
Console.WriteLine("Blog: " + blog.Url);
}
Mimo że ten kod wymaga tylko właściwości każdego bloga Url
, cała jednostka bloga jest pobierana, a niepotrzebne kolumny są przenoszone z bazy danych:
SELECT [b].[BlogId], [b].[CreationDate], [b].[Name], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
Można to zoptymalizować, używając Select
do poinformowania EF, które kolumny mają być wybrane.
await foreach (var blogName in context.Blogs.Select(b => b.Url).AsAsyncEnumerable())
{
Console.WriteLine("Blog: " + blogName);
}
Wynikowy kod SQL ściąga tylko potrzebne kolumny:
SELECT [b].[Url]
FROM [Blogs] AS [b]
Jeśli chcesz projektować więcej niż jedną kolumnę, przeprojektuj typ anonimowy języka C# z żądanymi właściwościami.
Należy pamiętać, że ta technika jest bardzo przydatna w przypadku zapytań tylko do odczytu, ale sytuacja jest bardziej skomplikowana, jeśli trzeba zaktualizować pobrane blogi, ponieważ śledzenie zmian w EF działa tylko z instancjami jednostek. Możliwe jest wykonywanie aktualizacji bez ładowania całych encji poprzez dołączenie zmodyfikowanego wystąpienia bloga i informowanie EF, które właściwości uległy zmianie, ale jest to bardziej zaawansowana technika, która może nie być opłacalna.
Ogranicz rozmiar zestawu wyników
Domyślnie zapytanie zwraca wszystkie wiersze zgodne z jego filtrami:
var blogsAll = await context.Posts
.Where(p => p.Title.StartsWith("A"))
.ToListAsync();
Ponieważ liczba zwracanych wierszy zależy od rzeczywistych danych w bazie danych, nie można wiedzieć, ile danych zostanie załadowanych z bazy danych, ile pamięci zostanie zajęta przez wyniki i ile dodatkowego obciążenia zostanie wygenerowane podczas przetwarzania tych wyników (np. wysyłając je do przeglądarki użytkownika za pośrednictwem sieci). Co najważniejsze, testowe bazy danych często zawierają małe dane, dzięki czemu wszystko działa dobrze podczas testowania, ale problemy z wydajnością pojawiają się nagle, gdy zapytanie zacznie działać na rzeczywistych danych i zwracanych jest wiele wierszy.
W związku z tym zwykle warto zastanowić się nad ograniczeniem liczby wyników:
var blogs25 = await context.Posts
.Where(p => p.Title.StartsWith("A"))
.Take(25)
.ToListAsync();
Co najmniej interfejs użytkownika może wyświetlić komunikat wskazujący, że w bazie danych może istnieć więcej wierszy (i zezwolić na ich pobieranie w inny sposób). Rozwiązanie pełnowymiarowe implementuje stronicowanie, w którym interfejs użytkownika wyświetla tylko określoną liczbę wierszy jednocześnie i umożliwia użytkownikom przejście do następnej strony zgodnie z potrzebami; Zobacz następną sekcję, aby uzyskać więcej informacji na temat sposobu efektywnego implementowania tej funkcji.
Wydajne stronicowanie
Stronicowanie odnosi się do pobierania wyników na stronach, a nie wszystkich jednocześnie; Zazwyczaj jest to wykonywane w przypadku dużych zestawów wyników, w których jest wyświetlany interfejs użytkownika, który umożliwia użytkownikowi przejście do następnej lub poprzedniej strony wyników. Typowym sposobem implementacji stronicowania z bazami danych jest użycie operatorów Skip
i Take
(OFFSET
i LIMIT
w języku SQL); chociaż taka implementacja jest intuicyjna, jest również dość nieefektywna. W przypadku stronicowania, które umożliwia przenoszenie jednej strony naraz (w przeciwieństwie do przechodzenia do dowolnych stron), rozważ użycie stronicowania zestawu kluczy .
Aby uzyskać więcej informacji, zobacz stronę dokumentacji dotyczącą stronicowania.
Unikaj eksplozji kartezjańskiej podczas ładowania powiązanych jednostek
W relacyjnych bazach danych wszystkie powiązane encje ładują się poprzez wprowadzenie JOIN w jednym zapytaniu.
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
Jeśli typowy blog zawiera wiele powiązanych wpisów, wiersze dla tych wpisów zduplikują informacje w blogu. Ta duplikacja prowadzi do tak zwanego problemu "wybuchu kartezjańskiego". W miarę ładowania większej liczby relacji jeden do wielu ilość zduplikowanych danych może rosnąć i niekorzystnie wpływać na wydajność aplikacji.
Program EF pozwala uniknąć tego efektu dzięki użyciu "podzielonych zapytań", które ładują powiązane jednostki za pośrednictwem oddzielnych zapytań. Aby uzyskać więcej informacji, przeczytaj dokumentację na temat rozdzielnych i pojedynczych zapytań.
Uwaga / Notatka
Bieżąca implementacja podzielonych zapytań realizuje podróż w obie strony dla każdego zapytania. Planujemy to poprawić w przyszłości i wykonać wszystkie zapytania w jednej rundzie.
Ładuj powiązane jednostki natychmiast, gdy jest to możliwe
Zaleca się przeczytanie dedykowanej strony dotyczącej powiązanych jednostek przed kontynuowaniem pracy z tą sekcją.
W przypadku czynienia z powiązanymi jednostkami zwykle wiemy z wyprzedzeniem, co musimy załadować: typowy przykład polega na załadowaniu określonego zestawu blogów wraz ze wszystkimi wpisami. W tych scenariuszach zawsze lepiej jest używać ładowania poprzedzającego, aby program EF mógł pobrać wszystkie wymagane dane w jednym przebiegu. Filtrowana funkcja dołączania umożliwia również ograniczenie powiązanych jednostek, które chcesz załadować, przy jednoczesnym zachowaniu chętnego procesu ładowania i w związku z tym możliwego do wykonania w ramach pojedynczej rundy:
using (var context = new BloggingContext())
{
var filteredBlogs = await context.Blogs
.Include(
blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToListAsync();
}
W innych scenariuszach możemy nie wiedzieć, której powiązanej jednostki będziemy potrzebować, zanim uzyskamy jej jednostkę główną. Na przykład podczas ładowania niektórych blogów może być konieczne skonsultowanie się z innym źródłem danych — prawdopodobnie usługą internetową — aby dowiedzieć się, czy jesteśmy zainteresowani wpisami w blogu. W takich przypadkach jawne lub leniwe ładowanie może służyć do pobierania powiązanych jednostek oddzielnie i wypełniania nawigacji Wpisy w blogu. Należy pamiętać, że ponieważ te metody nie stosują tzw. eager loading, wymagają dodatkowych zapytań do bazy danych, co jest źródłem spowolnienia; w zależności od konkretnego scenariusza może być bardziej wydajne, aby zawsze pobierać wszystkie wpisy, zamiast wykonywać dodatkowe zapytania i selektywnie pobierać tylko potrzebne wpisy.
Uważaj na leniwe ładowanie
Ładowanie leniwe często wydaje się bardzo przydatnym sposobem na napisanie logiki bazy danych, ponieważ program EF Core automatycznie ładuje powiązane jednostki z bazy danych, gdy są one używane przez kod. Pozwala to uniknąć ładowania powiązanych jednostek, które nie są potrzebne (na przykład jawne ładowanie), i pozornie zwalnia programistę z konieczności całkowitego radzenia sobie z powiązanymi jednostkami. Jednak leniwe ładowanie jest szczególnie podatne na generowanie niepotrzebnych dodatkowych zapytań, co może spowolnić działanie aplikacji.
Rozważ następujące kwestie:
foreach (var blog in await context.Blogs.ToListAsync())
{
foreach (var post in blog.Posts)
{
Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
}
}
Ten pozornie niewinny fragment kodu przechodzi przez wszystkie blogi i ich wpisy, wyświetlając je na ekranie. Włączenie logowania instrukcji w EF Core powoduje wyświetlenie następujących informacji:
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[@__p_0='1'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@__p_0='2'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@__p_0='3'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0
... and so on
Co się tu dzieje? Dlaczego wszystkie te zapytania są wysyłane dla powyższych prostych pętli? Z leniwym ładowaniem, posty w blogu są ładowane tylko wtedy (leniwo), gdy dostępna jest ich właściwość Posts; w związku z tym każda iteracja w wewnętrznej pętli foreach wyzwala dodatkowe zapytanie do bazy danych podczas osobnej rundy. W związku z tym po początkowym zapytaniu ładujący wszystkie blogi mamy kolejne zapytanie na blog, ładujący wszystkie jego wpisy; Jest to czasami nazywane problemem N+1 i może powodować bardzo znaczące problemy z wydajnością.
Zakładając, że będziemy potrzebować wszystkich wpisów blogów, warto zamiast tego użyć eager loading. Możemy użyć operatora Dołączanie , aby wykonać ładowanie, ale ponieważ potrzebujemy tylko adresów URL blogów (i powinniśmy załadować tylko to, co jest potrzebne). Użyjemy więc projekcji:
await foreach (var blog in context.Blogs.Select(b => new { b.Url, b.Posts }).AsAsyncEnumerable())
{
foreach (var post in blog.Posts)
{
Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
}
}
Spowoduje to pobranie wszystkich blogów w programie EF Core — wraz z ich wpisami — w jednym zapytaniu. W niektórych przypadkach może być również przydatne, aby uniknąć efektów eksplozji kartezjańskiej przy użyciu podzielonych zapytań.
Ostrzeżenie
Ponieważ ładowanie leniwe sprawia, że niezwykle łatwo przypadkowo spowodować wystąpienie problemu N+1, zaleca się unikać go. Ładowanie chętne lub jawne wyraźnie pokazuje w kodzie źródłowym, kiedy dochodzi do zapytania zwrotnego do bazy danych.
Buforowanie i przesyłanie strumieniowe
Buforowanie odnosi się do ładowania wszystkich wyników zapytania do pamięci, natomiast przesyłanie strumieniowe oznacza, że program EF przekazuje aplikacji jeden wynik za każdym razem, nigdy nie zawierający całego zestawu wyników w pamięci. Zasadniczo wymagania dotyczące pamięci zapytania przesyłania strumieniowego są stałe — są takie same, czy zapytanie zwraca 1 wiersz, czy 1000; z drugiej strony zapytanie buforujące wymaga większej ilości pamięci, tym więcej wierszy jest zwracanych. W przypadku zapytań, które powodują duże zestawy wyników, może to być ważny czynnik wydajności.
Czy zapytanie buforuje czy przesyła strumieniowo zależy od sposobu jego przetwarzania.
// ToList and ToArray cause the entire resultset to be buffered:
var blogsList = await context.Posts.Where(p => p.Title.StartsWith("A")).ToListAsync();
var blogsArray = await context.Posts.Where(p => p.Title.StartsWith("A")).ToArrayAsync();
// Foreach streams, processing one row at a time:
await foreach (var blog in context.Posts.Where(p => p.Title.StartsWith("A")).AsAsyncEnumerable())
{
// ...
}
// AsAsyncEnumerable also streams, allowing you to execute LINQ operators on the client-side:
var doubleFilteredBlogs = context.Posts
.Where(p => p.Title.StartsWith("A")) // Translated to SQL and executed in the database
.AsAsyncEnumerable()
.Where(p => SomeDotNetMethod(p)); // Executed at the client on all database results
Jeśli zapytania zwracają tylko kilka wyników, prawdopodobnie nie musisz się tym martwić. Jeśli jednak zapytanie może zwrócić dużą liczbę wierszy, warto rozważyć strumieniowanie zamiast buforowania.
Uwaga / Notatka
Unikaj używania ToList lub ToArray jeśli zamierzasz użyć innego operatora LINQ w wyniku — będzie to niepotrzebnie buforować wszystkie wyniki w pamięci. Użyj AsEnumerable zamiast tego.
Buforowanie wewnętrzne przez Entity Framework (EF)
W niektórych sytuacjach program EF wewnętrznie buforuje zestaw wyników niezależnie od sposobu oceny zapytania. Dwa przypadki, w których tak się dzieje, to:
- Gdy zastosowana jest strategia ponawiania wykonania. Jest to wykonywane, aby upewnić się, że te same wyniki są zwracane, jeśli zapytanie zostanie ponowione później.
- Gdy używane jest zapytanie podzielone, zestawy wyników wszystkich zapytań oprócz ostatniego są buforowane — chyba że usługa MARS (wiele aktywnych zestawów wyników) jest włączona na serwerze SQL Server. Jest to spowodowane tym, że zwykle nie można jednocześnie uaktywnić wielu zestawów wyników zapytań.
Należy pamiętać, że to wewnętrzne buforowanie występuje oprócz buforowania, które wprowadzasz za pomocą operatorów LINQ. Jeśli na przykład używasz ToList na zapytaniu i jest zastosowana strategia ponawiania wykonywania, wynikowy zestaw danych zostanie załadowany do pamięci dwa razy: raz wewnętrznie przez program EF, a raz przez ToList.
Śledzenie, brak śledzenia i rozpoznawanie tożsamości
Zaleca się przeczytanie dedykowanej strony na temat śledzenia i braku śledzenia przed kontynuowaniem tej sekcji.
Program EF domyślnie śledzi wystąpienia jednostek, dzięki czemu zmiany w nich są wykrywane i utrwalane, gdy wywoływane jest SaveChanges. Innym efektem śledzenia zapytań jest to, że program EF wykrywa, czy wystąpienie zostało już załadowane dla danych, i automatycznie zwróci to śledzone wystąpienie zamiast zwracać nowe; jest to nazywane rozpoznawaniem tożsamości. Z perspektywy wydajności śledzenie zmian oznacza następujące kwestie:
- Program EF wewnętrznie utrzymuje słownik śledzonych wystąpień. Po załadowaniu nowych danych program EF sprawdza słownik, aby sprawdzić, czy wystąpienie jest już śledzone pod kątem klucza tej jednostki (rozpoznawanie tożsamości). Konserwacja słownika i wyszukiwania zajmują trochę czasu podczas ładowania wyników zapytania.
- Przed przekazaniem załadowanego wystąpienia do aplikacji, EF wykonuje migawki tego wystąpienia i przechowuje je wewnętrznie. Kiedy wywołane jest SaveChanges, wystąpienie aplikacji jest porównywane z migawką w celu odnalezienia zmian, które mają być zapisane. Migawka zużywa więcej pamięci, a sam proces robienia migawki zajmuje trochę czasu; czasami można określić różne, prawdopodobnie bardziej wydajne zachowania migawki za pomocą porównań wartości lub użyć pełnomocników śledzenia zmian, aby całkowicie pominąć proces robienia migawki (to jednak ma swoje wady).
W scenariuszach tylko do odczytu, w których zmiany nie są zapisywane z powrotem w bazie danych, powyższe obciążenia można uniknąć przy użyciu zapytań bez śledzenia. Jednak ponieważ zapytania bez śledzenia nie wykonują rozpoznawania tożsamości, wiersz bazy danych, do którego odwołuje się wiele innych załadowanych wierszy, zostanie zmaterializowany jako różne wystąpienia.
Aby zilustrować, załóżmy, że ładujemy dużą liczbę wpisów z bazy danych, a także blog, do których odwołuje się każdy wpis. Jeśli 100 wpisów odwołuje się do tego samego bloga, zapytanie śledzące wykryje to poprzez rozpoznawanie tożsamości. Wszystkie wystąpienia wpisów będą odwoływać się do tego samego zdeduplikowanego wystąpienia bloga. Natomiast zapytanie bez śledzenia duplikuje ten sam blog 100 razy — a odpowiednio należy napisać kod aplikacji.
Poniżej przedstawiono wyniki testu porównawczego porównującego śledzenie z brakiem śledzenia dla zapytania ładującego 10 blogów, z których każdy zawiera 20 wpisów. Kod źródłowy jest dostępny tutaj, możesz używać go jako podstawy do własnych pomiarów.
Metoda | Dzienniki numerów | LiczbaPostówNaBlog | Znaczyć | Błąd | StdDev | Mediana | Współczynnik | RatioSD | Gen 0 | Gen 1 | Gen 2 | Przydzielone |
---|---|---|---|---|---|---|---|---|---|---|---|---|
AsTracking | 10 | 20 | 1414.7 | 27.20 µs | 45.44 nas | 1405,5 nas | 1.00 | 0,00 | 60,5469 | 13.6719 | - | 380.11 KB |
AsNoTracking | 10 | 20 | 993.3 nas | 24.04 us | 65.40 nas | 966.2 nas | 0.71 | 0.05 | 37.1094 | 6.8359 | - | 232.89 KB |
Na koniec, możliwe jest przeprowadzanie aktualizacji bez obciążenia śledzenia zmian, używając zapytania bez śledzenia, a następnie dołączając zwrócone wystąpienie do kontekstu i określając, jakie zmiany należy wprowadzić. Spowoduje to przeniesienie obciążenia śledzenia zmian z ef do użytkownika i powinno być podejmowane tylko wtedy, gdy obciążenie śledzenia zmian zostało wykazane jako niedopuszczalne za pośrednictwem profilowania lub porównywania porównawczego.
Korzystanie z zapytań SQL
W niektórych przypadkach bardziej zoptymalizowany język SQL istnieje dla zapytania, którego program EF nie generuje. Może się tak zdarzyć, gdy konstrukcja SQL jest rozszerzeniem specyficznym dla bazy danych, która nie jest obsługiwana, lub po prostu dlatego, że program EF nie tłumaczy się jeszcze na nią. W takich przypadkach pisanie kodu SQL ręcznie może zapewnić znaczny wzrost wydajności, a program EF obsługuje kilka sposobów, aby to zrobić.
- Użyj zapytań SQL bezpośrednio w zapytaniu, np. za pośrednictwem .FromSqlRaw Platforma EF umożliwia nawet tworzenie kodu SQL przy użyciu zwykłych zapytań LINQ, co umożliwia wyrażenie tylko części zapytania w języku SQL. Jest to dobra technika, gdy usługa SQL musi być używana tylko w jednym zapytaniu w bazie kodu.
- Zdefiniuj funkcję zdefiniowaną przez użytkownika (UDF), a następnie wywołaj tę funkcję z zapytań. Należy pamiętać, że EF pozwala funkcjom zdefiniowanym przez użytkownika na zwracanie pełnych zestawów wyników — są one nazywane funkcjami tablicowymi (TVFs) — oraz umożliwia mapowanie elementu typu
DbSet
na funkcję, co sprawia, że wygląda ona jak kolejna tabela. - Zdefiniuj widok bazy danych i zapytanie z niego w zapytaniach. Należy pamiętać, że w przeciwieństwie do funkcji widoki nie mogą akceptować parametrów.
Uwaga / Notatka
Nieprzetworzone dane SQL powinny być zwykle używane jako ostateczność, po upewnieniu się, że program EF nie może wygenerować żądanego kodu SQL, a gdy wydajność jest wystarczająco ważna dla danego zapytania, aby je uzasadnić. Korzystanie z surowego SQL przynosi znaczne wady utrzymania.
Programowanie asynchroniczne
Ogólnie rzecz biorąc, aby aplikacja była skalowalna, ważne jest, aby zawsze używać asynchronicznych interfejsów API, a nie synchronicznych (np. SaveChangesAsyncSaveChanges). Synchroniczne interfejsy API blokują wątek przez czas trwania operacji we/wy bazy danych, zwiększając zapotrzebowanie na wątki i liczbę przełączeń kontekstu wątku, które muszą wystąpić.
Aby uzyskać więcej informacji, zobacz stronę dotyczącą programowania asynchronicznego.
Ostrzeżenie
Unikaj mieszania synchronicznego i asynchronicznego kodu w tej samej aplikacji — łatwo jest przypadkowo wywołać subtelne problemy z niewystarczającymi zasobami puli wątków.
Ostrzeżenie
Implementacja asynchronicznego Microsoft.Data.SqlClient niestety ma pewne znane problemy (np. #593, #601i inne). Jeśli występują nieoczekiwane problemy z wydajnością, spróbuj zamiast tego użyć polecenia synchronizacji, szczególnie w przypadku obsługi dużych wartości tekstowych lub binarnych.
Dodatkowe zasoby
- Aby uzyskać dodatkowe tematy dotyczące wydajnego wykonywania zapytań, zobacz stronę zaawansowanych tematów wydajności.
- Zapoznaj się z sekcją wydajności na stronie dokumentacji porównywania wartości null, gdzie znajdziesz najlepsze praktyki dotyczące porównywania wartości null.