Dela via


Klient- och serverutvärdering

Som en allmän regel försöker Entity Framework Core utvärdera en fråga på servern så mycket som möjligt. EF Core konverterar delar av frågan till parametrar som kan utvärderas på klientsidan. Resten av frågan (tillsammans med de genererade parametrarna) ges till databasprovidern för att fastställa motsvarande databasfråga som ska utvärderas på servern. EF Core stöder partiell klientutvärdering i den övergripande projektionen (som i huvudsak är det sista anropet till Select()). Om den översta projektionen i frågan inte kan översättas till servern hämtar EF Core nödvändiga data från servern och utvärderar återstående delar av frågan på klienten. Om EF Core identifierar ett uttryck, på någon annan plats än på översta nivån av projektionen, som inte kan översättas till servern, utlöses ett körningsundantag. Se Hur frågor fungerar för att förstå hur EF Core avgör vad som inte kan översättas till servern.

Anmärkning

Före version 3.0 hade Entity Framework Core stöd för klientutvärdering var som helst i frågan. Mer information finns i avsnittet tidigare versioner.

Tips/Råd

Du kan visa den här artikelns exempel på GitHub.

Klientutvärdering i högsta nivåprojektionen

I följande exempel används en hjälpmetod för att standardisera URL:er för bloggar som returneras från en SQL Server-databas. Eftersom SQL Server-providern inte har någon insikt i hur den här metoden implementeras går det inte att översätta den till SQL. Alla andra aspekter av frågeställningen utvärderas i databasen; att skicka värdet som returneras URL genom den här metoden görs på klienten.

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;
}

Klientutvärdering som inte stöds

Även om klientutvärdering är användbar kan det leda till dåliga prestanda ibland. Tänk på följande fråga, där hjälpmetoden nu används i ett where-filter. Eftersom filtret inte kan användas i databasen måste alla data hämtas till minnet för att tillämpa filtret på klienten. Baserat på filtret och mängden data på servern kan klientutvärderingen resultera i dåliga prestanda. Entity Framework Core blockerar därför en sådan klientutvärdering och utlöser ett körningsundundatag.

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

Explicit klientutvärdering

Du kan behöva tvinga in klientutvärdering explicit i vissa fall som att följa

  • Mängden data är liten för att utvärdering på klienten inte ska medföra en stor prestandapåverkan.
  • LINQ-operatorn som används har ingen översättning på serversidan.

I sådana fall kan du uttryckligen välja klientutvärdering genom att anropa metoder som AsEnumerable eller ToList (AsAsyncEnumerable eller ToListAsync för asynkronisering). Genom att använda AsEnumerable skulle du strömma resultatet, men om du använder ToList det skulle det orsaka buffring genom att skapa en lista som också tar ytterligare minne. Om du räknar upp flera gånger hjälper det dock att lagra resultat i en lista mer eftersom det bara finns en fråga i databasen. Beroende på den specifika användningen bör du utvärdera vilken metod som är mer användbar för ärendet.

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

Tips/Råd

Om du använder AsAsyncEnumerable och vill skriva frågan ytterligare på klientsidan kan du använda biblioteket System.Interactive.Async som definierar operatorer för asynkrona uppräkningar. Mer information finns i klientsidans linq-operatorer.

Potentiell minnesläcka i klientutvärdering

Eftersom frågeöversättning och kompilering är dyra cachelagrar EF Core den kompilerade frågeplanen. Det cachelagrade delegerade objektet kan använda klientkod när projektion på högsta nivå klientutvärderas. EF Core genererar parametrar för de klientberäknarede delarna av trädet och återanvänder frågeplanen genom att ersätta parametervärdena. Men vissa konstanter i uttrycksträdet kan inte konverteras till parametrar. Om det cachelagrade ombudet innehåller sådana konstanter kan dessa objekt inte vara skräpinsamlade eftersom de fortfarande refereras. Om ett sådant objekt innehåller en DbContext eller andra tjänster i det kan det leda till att minnesanvändningen för appen växer över tid. Det här beteendet är vanligtvis ett tecken på en minnesläcka. EF Core utlöser ett undantag när det stöter på konstanter av en typ som inte kan mappas med hjälp av den aktuella databasprovidern. Vanliga orsaker och deras lösningar är följande:

  • Använda en instansmetod: När du använder instansmetoder i en klientprojektion innehåller uttrycksträdet en konstant av instansen. Om metoden inte använder några data från instansen kan du överväga att göra metoden statisk. Om du behöver instansdata i metodtexten skickar du specifika data som ett argument till metoden.
  • Skicka konstanta argument till metoden: Det här fallet uppstår vanligtvis med hjälp this av ett argument till klientmetoden. Överväg att dela in argumentet i flera skalära argument, som kan mappas av databasprovidern.
  • Andra konstanter: Om en konstant uppstår i andra fall kan du utvärdera om konstanten behövs i bearbetningen. Om det är nödvändigt att ha konstanten, eller om du inte kan använda en lösning från ovanstående fall, skapar du en lokal variabel för att lagra värdet och använda den lokala variabeln i frågan. EF Core konverterar den lokala variabeln till en parameter.

Tidigare versioner

Följande avsnitt gäller för EF Core-versioner före 3.0.

Äldre EF Core-versioner stödde klientutvärdering i vilken del av frågan som helst – inte bara i den översta nivås projektion. Det är därför frågor som liknar en som publicerats under avsnittet Klientutvärdering som inte stöds fungerade korrekt. Eftersom det här beteendet kan orsaka obemärkta prestandaproblem, registrerade EF Core en varning för klientvärdering. Mer information om hur du visar loggningsutdata finns i Loggning.

Du kan också använda EF Core för att ändra standardbeteendet till att antingen utlösa ett undantag eller göra ingenting när du gör klientutvärdering (förutom i projektionen). Undantaget som utlöser beteendet skulle göra att det liknar beteendet i 3.0. Om du vill ändra beteendet måste du konfigurera varningar när du konfigurerar alternativen för din kontext – vanligtvis i DbContext.OnConfiguring, eller i Startup.cs om du använder 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));
}