Delen via


Client versus server-evaluatie

Als algemene regel probeert Entity Framework Core zo veel mogelijk een query op de server te evalueren. EF Core converteert delen van de query naar parameters, die aan de clientzijde kunnen worden geëvalueerd. De rest van de query (samen met de gegenereerde parameters) wordt aan de databaseprovider gegeven om de equivalente databasequery te bepalen die moet worden geëvalueerd op de server. EF Core ondersteunt gedeeltelijke clientevaluatie in de projectie op het hoogste niveau (in wezen de laatste aanroep naar Select()). Als de projectie op het hoogste niveau in de query niet naar de server kan worden vertaald, haalt EF Core alle vereiste gegevens op van de server en evalueert de resterende onderdelen van de query op de client. Als EF Core een expressie detecteert, op een andere plaats dan de projectie op het hoogste niveau, die niet naar de server kan worden vertaald, wordt er een runtime-uitzondering gegenereerd. Zie Hoe query's werken om te begrijpen hoe EF Core bepaalt wat er niet kan worden vertaald naar de server.

Opmerking

Vóór versie 3.0 ondersteunde Entity Framework Core-clientevaluatie overal in de query. Zie de sectie vorige versies voor meer informatie.

Aanbeveling

U kunt het voorbeeld van dit artikel bekijken op GitHub.

Evaluatie van de klant op het hoogste niveau

In het volgende voorbeeld wordt een helpermethode gebruikt voor het standaardiseren van URL's voor blogs, die worden geretourneerd vanuit een SQL Server-database. Omdat de SQL Server-provider geen inzicht heeft in de manier waarop deze methode wordt geïmplementeerd, is het niet mogelijk om deze te vertalen naar SQL. Alle andere aspecten van de query worden geëvalueerd in de database, maar het doorgeven van de geretourneerde URL gegevens via deze methode wordt uitgevoerd op de client.

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

Evaluatie van de cliënt wordt niet ondersteund

Hoewel clientevaluatie nuttig is, kan dit soms leiden tot slechte prestaties. Houd rekening met de volgende query, waarin de helpermethode nu wordt gebruikt in een where-filter. Omdat het filter niet kan worden toegepast in de database, moeten alle gegevens in het geheugen worden opgehaald om het filter op de client toe te passen. Op basis van het filter en de hoeveelheid gegevens op de server kan de clientevaluatie leiden tot slechte prestaties. Entity Framework Core blokkeert deze clientevaluatie dus en genereert een runtime-uitzondering.

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

Expliciete evaluatie van klanten

Mogelijk moet u de clientevaluatie expliciet afdwingen in bepaalde gevallen, zoals hieronder

  • De hoeveelheid gegevens is klein, zodat het evalueren op de client niet tot een enorme prestatievermindering leidt.
  • De LINQ-operator die wordt gebruikt, heeft geen vertaling aan de serverzijde.

In dergelijke gevallen kunt u expliciet kiezen voor clientevaluatie door methoden aan te roepen, zoals AsEnumerable of ToList (AsAsyncEnumerable of ToListAsync voor asynchroon). Door AsEnumerable te gebruiken, zou je de resultaten streamen, maar het gebruik van ToList zou buffers veroorzaken door een lijst te maken, waardoor er ook extra geheugen nodig is. Maar als u herhaaldelijk opsomt, is het opslaan van resultaten in een lijst voordelig omdat slechts één databasequery noodzakelijk is. Afhankelijk van het specifieke gebruik moet u evalueren welke methode nuttiger is voor de case.

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

Aanbeveling

Als u AsAsyncEnumerable gebruikt en de query verder aan de clientzijde wilt opstellen, kunt u de System.Interactive.Async-bibliotheek gebruiken, waarin operators voor asynchrone enumerables worden gedefinieerd. Zie linq-operators aan clientzijde voor meer informatie.

Potentieel geheugenlek in clientevaluatie

Omdat het vertalen en compileren van query's duur zijn, slaat EF Core het gecompileerde queryplan in de cache op. De gedelegeerde in de cache kan gebruik maken van clientcode tijdens de projectie-evaluatie op cliëntniveau. EF Core genereert parameters voor de door de client geëvalueerde onderdelen van de structuur en hergebruikt het queryplan door de parameterwaarden te vervangen. Maar bepaalde constanten in de expressiestructuur kunnen niet worden geconverteerd naar parameters. Als er constanten zijn opgeslagen in de gedelegeerde cache, kunnen deze objecten niet worden verwijderd omdat er nog steeds naar wordt verwezen. Als een dergelijk object een DbContext of andere services bevat, kan dit ertoe leiden dat het geheugengebruik van de app na verloop van tijd toeneemt. Dit gedrag is over het algemeen een teken van een geheugenlek. EF Core werpt een uitzondering wanneer het constanten tegenkomt van een type dat niet kan worden toegewezen door de huidige databaseprovider. Veelvoorkomende oorzaken en hun oplossingen zijn als volgt:

  • Met behulp van een instantiemethode: Wanneer u exemplaarmethoden gebruikt in een clientprojectie, bevat de expressiestructuur een constante van het exemplaar. Als uw methode geen gegevens van het exemplaar gebruikt, kunt u overwegen om de methode statisch te maken. Als u instantiegegevens in de hoofdtekst van de methode nodig hebt, geeft u de specifieke gegevens als argument door aan de methode.
  • Constante argumenten doorgeven aan methode: dit geval ontstaat meestal door gebruik te maken this van een argument aan clientmethode. U kunt het argument splitsen in meerdere scalaire argumenten, die kunnen worden toegewezen door de databaseprovider.
  • Andere constanten: Als een constante in een ander geval tegenkomt, kunt u evalueren of de constante nodig is in de verwerking. Als het nodig is om de constante te hebben of als u geen oplossing uit de bovenstaande gevallen kunt gebruiken, maakt u een lokale variabele om de waarde op te slaan en lokale variabele in de query te gebruiken. Ef Core converteert de lokale variabele naar een parameter.

Vorige versies

De volgende sectie is van toepassing op EF Core-versies vóór 3.0.

Oudere EF Core-versies ondersteunen clientevaluatie in elk deel van de query, niet alleen de projectie op het hoogste niveau. Daarom werkten query's die vergelijkbaar zijn met een query die is gepost in de sectie Niet-ondersteunde clientevaluatie correct. Omdat dit gedrag onopgemerkte prestatieproblemen kan veroorzaken, heeft EF Core een waarschuwing voor clientevaluatie vastgelegd. Zie Logging voor meer informatie over het weergeven van logging uitvoer.

Met EF Core kunt u desgewenst het standaardgedrag wijzigen om een uitzondering te genereren of niets te doen bij het uitvoeren van de clientevaluatie (met uitzondering van de projectie). Het gedrag van het werpen van uitzonderingen zou het vergelijkbaar maken met het gedrag in 3.0. Als u het gedrag wilt wijzigen, moet u waarschuwingen configureren tijdens het instellen van de opties voor uw context, meestal in DbContext.OnConfiguringof als Startup.cs u ASP.NET Core gebruikt.

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