Udostępnij przez


Klient a ocena serwera

Ogólnie rzecz biorąc, Entity Framework Core próbuje ocenić zapytanie na serwerze w jak największym stopniu. Program EF Core konwertuje części zapytania na parametry, które można ocenić po stronie klienta. Pozostała część zapytania (wraz z wygenerowanymi parametrami) jest podawana dostawcy bazy danych w celu określenia równoważnego zapytania bazy danych do oceny na serwerze. EF Core obsługuje częściową ewaluację po stronie klienta w projekcji na najwyższym poziomie (zasadniczo podczas ostatniego wywołania Select()). Jeśli projekcja najwyższego poziomu w zapytaniu nie może zostać przetłumaczona na serwer, program EF Core pobierze wszystkie wymagane dane z serwera i oceni pozostałe części zapytania na kliencie. Jeśli program EF Core wykryje wyrażenie, w dowolnym miejscu innym niż projekcja najwyższego poziomu, którego nie można przetłumaczyć na serwer, zgłasza wyjątek środowiska uruchomieniowego. Zobacz Jak działają zapytania , aby zrozumieć, jak program EF Core określa, czego nie można przetłumaczyć na serwer.

Uwaga / Notatka

Przed wersją 3.0 program Entity Framework Core obsługuje ocenę klienta w dowolnym miejscu w zapytaniu. Aby uzyskać więcej informacji, zobacz sekcję poprzednich wersji.

Wskazówka

Przykład z tego artykułu można zobaczyć w witrynie GitHub.

Ocena klienta w projekcji najwyższego poziomu

W poniższym przykładzie metoda pomocnika służy do standaryzacji adresów URL dla blogów, które są zwracane z bazy danych programu SQL Server. Ponieważ dostawca programu SQL Server nie ma wglądu w sposób implementacji tej metody, nie można go przetłumaczyć na język SQL. Wszystkie inne aspekty zapytania są oceniane w bazie danych, ale zwrócone URL jest przekazywane przez tę metodę na kliencie.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
    .ToListAsync();
public static string StandardizeUrl(string url)
{
    url = url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

Nieobsługiwana ocena klienta

Chociaż ocena klienta jest przydatna, czasami może to spowodować niską wydajność. Rozważ następujące zapytanie, w którym metoda pomocnicza jest teraz używana w filtrze WHERE. Ponieważ nie można zastosować filtru w bazie danych, wszystkie dane należy ściągnąć do pamięci, aby zastosować filtr na kliencie. Na podstawie filtru i ilości danych na serwerze ocena klienta może spowodować niską wydajność. Dlatego program Entity Framework Core blokuje taką ocenę klienta i zgłasza wyjątek środowiska uruchomieniowego.

var blogs = await context.Blogs
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToListAsync();

Jawna ocena klienta

Może być konieczne wymusienie jawnej oceny klienta w niektórych przypadkach, takich jak następujące

  • Ilość danych jest niewielka, aby ocena na kliencie nie powodowała znaczącego spadku wydajności.
  • Używany operator LINQ nie ma tłumaczenia po stronie serwera.

W takich przypadkach można jawnie wyrazić zgodę na ocenę klienta, wywołując metody takie jak AsEnumerable lub ToList (AsAsyncEnumerable lub ToListAsync asynchroniczne). Dzięki użyciu AsEnumerable można przesyłać strumieniowo wyniki, ale użycie ToList spowoduje buforowanie poprzez utworzenie listy, co również zużywa dodatkową pamięć. Chociaż jeśli wyliczasz wiele razy, przechowywanie wyników na liście pomaga bardziej, ponieważ istnieje tylko jedno zapytanie do bazy danych. W zależności od konkretnego użycia należy ocenić, która metoda jest bardziej przydatna w danym przypadku.

var blogs = context.Blogs
    .AsAsyncEnumerable()
    .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
    .ToListAsync();

Wskazówka

Jeśli używasz AsAsyncEnumerable i chcesz utworzyć zapytanie dalej po stronie klienta, możesz użyć biblioteki System.Interactive.Async, która definiuje operatory dla asynchronicznych wyliczeń. Aby uzyskać więcej informacji, zobacz operatory LINQ po stronie klienta.

Potencjalny wyciek pamięci w ocenie klienta

Ponieważ tłumaczenie i kompilacja zapytań są kosztowne, program EF Core buforuje skompilowany plan zapytania. Buforowany delegat może używać kodu klienckiego podczas oceniania projekcji na najwyższym poziomie. EF Core generuje parametry dla części drzewa, które są oceniane przez klienta, i ponownie używa planu zapytania, zastępując wartości parametrów. Jednak niektórych stałych w drzewie wyrażeń nie można przekonwertować na parametry. Jeśli buforowany delegat zawiera takie stałe, nie można odzyskać pamięci tych obiektów, ponieważ są one nadal przywoływane. Jeśli taki obiekt zawiera obiekt DbContext lub inne usługi, może to spowodować wzrost użycia pamięci aplikacji w czasie. To zachowanie jest zazwyczaj oznaką przecieku pamięci. Program EF Core zgłasza wyjątek za każdym razem, gdy występuje stałe typu, którego nie można zamapować przy użyciu bieżącego dostawcy bazy danych. Typowe przyczyny i ich rozwiązania są następujące:

  • Używając metody instancji: w przypadku używania metod instancji w projekcji klienta drzewo wyrażeń zawiera stałą instancji. Jeśli metoda nie używa żadnych danych z wystąpienia, rozważ utworzenie metody statycznej. Jeśli potrzebujesz danych wystąpienia w treści metody, przekaż określone dane jako argument do metody.
  • Przekazywanie argumentów stałych do metody: ten przypadek występuje zazwyczaj przy użyciu this argumentu do metody klienta. Rozważ podzielenie argumentu na wiele argumentów skalarnych, które mogą być mapowane przez dostawcę bazy danych.
  • Inne stałe: jeśli stała występuje w każdym innym przypadku, możesz ocenić, czy stała jest potrzebna w przetwarzaniu. Jeśli konieczne jest posiadanie stałej lub jeśli nie możesz użyć rozwiązania z powyższych przypadków, utwórz zmienną lokalną do przechowywania wartości i użyj zmiennej lokalnej w zapytaniu. Program EF Core przekonwertuje zmienną lokalną na parametr.

Poprzednie wersje

Poniższa sekcja dotyczy wersji programu EF Core przed wersją 3.0.

Starsze wersje EF Core obsługiwały ocenę po stronie klienta w dowolnej części zapytania — nie tylko w projekcji na najwyższym poziomie. Dlatego zapytania podobne do jednych z zapytań opublikowanych w sekcji Nieobsługiwana ocena klienta działały prawidłowo. Ponieważ to zachowanie może powodować niezauważone problemy z wydajnością, program EF Core zarejestrował ostrzeżenie dotyczące oceny klienta. Aby uzyskać więcej informacji na temat wyświetlania danych wyjściowych rejestrowania, zobacz Rejestrowanie.

Opcjonalnie EF Core umożliwia zmianę domyślnego zachowania na zgłoszenie wyjątku lub niepodejmowanie działań podczas oceny po stronie klienta, z wyjątkiem przypadków w projekcji. Zachowanie polegające na zgłaszaniu wyjątków uczyniłoby to podobnym do zachowania w wersji 3.0. Aby zmienić zachowanie, należy skonfigurować ostrzeżenia podczas konfigurowania opcji kontekstu — zazwyczaj w DbContext.OnConfiguring, lub w Startup.cs jeśli używasz ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}