Freigeben über


Client- und Serverauswertung

In der Regel versucht Entity Framework Core, eine Abfrage auf dem Server so weit wie möglich auszuwerten. EF Core konvertiert Teile der Abfrage in Parameter, die sie auf der Clientseite auswerten kann. Der Rest der Abfrage (zusammen mit den generierten Parametern) wird dem Datenbankanbieter übergeben, um die entsprechende Datenbankabfrage zu bestimmen, die auf dem Server ausgewertet werden soll. EF Core unterstützt die partielle Clientauswertung in der Projektion auf oberster Ebene (im Wesentlichen der letzte Aufruf von Select()). Wenn die Projektion auf oberster Ebene in der Abfrage nicht auf den Server übersetzt werden kann, ruft EF Core alle erforderlichen Daten vom Server ab und wertet verbleibende Teile der Abfrage auf dem Client aus. Wenn EF Core einen Ausdruck an einer anderen Stelle als in der obersten Ebene der Projektion erkennt, der nicht auf den Server übersetzt werden kann, wird eine Laufzeitausnahme ausgelöst. Erfahren Sie , wie Abfragen funktionieren , um zu verstehen, wie EF Core bestimmt, was nicht auf den Server übersetzt werden kann.

Hinweis

Vor Version 3.0 unterstützte Entity Framework Core die Clientauswertung an einer beliebigen Stelle in der Abfrage. Weitere Informationen finden Sie im Abschnitt mit den vorherigen Versionen.

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.

Kundenauswertung in der Prognose auf höchster Ebene

Im folgenden Beispiel wird eine Hilfsmethode verwendet, um URLs für Blogs zu standardisieren, die aus einer SQL Server-Datenbank zurückgegeben werden. Da der SQL Server-Anbieter keinen Einblick in die Implementierung dieser Methode hat, ist es nicht möglich, sie in SQL zu übersetzen. Alle anderen Aspekte der Abfrage werden in der Datenbank ausgewertet, aber das Übergeben des zurückgegebenen URL erfolgt auf dem 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;
}

Nicht unterstützte Clientbewertung

Während die Clientauswertung nützlich ist, kann dies manchmal zu einer schlechten Leistung führen. Betrachten Sie die folgende Abfrage, in der die Hilfsmethode jetzt in einem Filter verwendet wird. Da der Filter nicht in der Datenbank angewendet werden kann, müssen alle Daten in den Arbeitsspeicher abgerufen werden, um den Filter auf den Client anzuwenden. Basierend auf dem Filter und der Datenmenge auf dem Server kann die Clientauswertung zu einer schlechten Leistung führen. Daher blockiert Entity Framework Core eine solche Clientauswertung und löst eine Laufzeitausnahme aus.

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

Explizite Kundenauswertung

Möglicherweise müssen Sie in bestimmten Fällen die Clientbewertung explizit erzwingen, z. B.

  • Die Datenmenge ist klein, sodass die Auswertung auf dem Client keine enorme Leistungseinbußen verursacht.
  • Der verwendete LINQ-Operator hat keine serverseitige Übersetzung.

In solchen Fällen können Sie sich explizit für die Clientauswertung entscheiden, indem Sie Methoden wie AsEnumerable oder ToList (AsAsyncEnumerable oder ToListAsync für asynchron) aufrufen. Durch die Verwendung von AsEnumerable würden Sie die Ergebnisse streamen, während die Verwendung von ToList zu Pufferungen führt, indem eine Liste erstellt wird, was auch zusätzlichen Arbeitsspeicher benötigt. Wenn Sie jedoch mehrmals aufzählen, hilft das Speichern von Ergebnissen in einer Liste mehr, da nur eine Abfrage für die Datenbank vorhanden ist. Je nach bestimmter Verwendung sollten Sie bewerten, welche Methode für den Fall nützlicher ist.

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

Tipp

Wenn Sie AsAsyncEnumerable verwenden und die Abfrage weiter auf der Clientseite entwickeln möchten, können Sie die System.Interactive.Async-Bibliothek verwenden, die Operatoren für asynchrone Enumerables definiert. Weitere Informationen finden Sie unter clientseitige Linq-Operatoren.

Potenzielles Speicherleck in der Klientenbewertung

Da Abfrageübersetzung und Kompilierung teuer sind, speichert EF Core den kompilierten Abfrageplan zwischen. Der zwischengespeicherte Delegat kann während der Evaluierung der Projektion auf höchsten Ebene Clientcode verwenden. EF Core generiert Parameter für die Teile des Baums, die vom Client ausgewertet werden, und nutzt den Abfrageplan erneut, indem er die Parameterwerte ersetzt. Bestimmte Konstanten in der Ausdrucksstruktur können jedoch nicht in Parameter konvertiert werden. Wenn der zwischengespeicherte Delegat solche Konstanten enthält, können diese Objekte nicht vom Garbage Collector entfernt werden, da weiterhin darauf verwiesen wird. Wenn ein solches Objekt einen DbContext oder andere Dienste enthält, kann dies dazu führen, dass die Speicherauslastung der App im Laufe der Zeit zunimmt. Dieses Verhalten ist in der Regel ein Anzeichen für einen Speicherverlust. EF Core löst eine Ausnahme aus, wenn konstanten eines Typs vorhanden sind, die nicht mithilfe des aktuellen Datenbankanbieters zugeordnet werden können. Häufige Ursachen und ihre Lösungen sind wie folgt:

  • Verwenden einer Instanzmethode: Wenn Instanzmethoden in einer Clientprojektion verwendet werden, enthält der Ausdrucksbaum eine Konstante der Instanz. Wenn Ihre Methode keine Daten aus der Instanz verwendet, sollten Sie die Methode statisch machen. Wenn Sie Instanzdaten im Methodentext benötigen, übergeben Sie die spezifischen Daten als Argument an die Methode.
  • Übergeben konstanter Argumente an die Methode: Dieser Fall tritt in der Regel mithilfe eines this Arguments für die Clientmethode auf. Erwägen Sie, das Argument in mehrere skalare Argumente aufzuteilen, die vom Datenbankanbieter zugeordnet werden können.
  • Andere Konstanten: Wenn in einem anderen Fall eine Konstante angezeigt wird, können Sie auswerten, ob die Konstante in der Verarbeitung benötigt wird. Wenn die Konstante erforderlich ist oder Sie eine Lösung aus den oben genannten Fällen nicht verwenden können, erstellen Sie eine lokale Variable, um den Wert zu speichern und die lokale Variable in der Abfrage zu verwenden. EF Core konvertiert die lokale Variable in einen Parameter.

Vorherige Versionen

Der folgende Abschnitt gilt für EF Core-Versionen vor 3.0.

Ältere EF Core-Versionen unterstützten die Clientauswertung in jedem Teil der Abfrage – nicht nur die Projektion auf oberster Ebene. Aus diesem Grund funktionierten Abfragen ähnlich wie im Abschnitt " Nicht unterstützte Clientauswertung " ordnungsgemäß. Da diese Verhaltensweise zu unbemerkten Leistungsproblemen führen kann, hat EF Core eine Clientauswertungswarnung protokolliert. Weitere Informationen zum Anzeigen der Protokollierungsausgabe finden Sie unter Protokollierung.

EF Core erlaubte es Ihnen optional, das Standardverhalten so zu ändern, dass entweder eine Ausnahme ausgelöst wird oder nichts geschieht, wenn eine Clientauswertung durchgeführt wird, außer in der Projektion. Das Verhalten beim Werfen von Ausnahmen würde dem Verhalten in 3.0 ähneln. Um das Verhalten zu ändern, müssen Sie Warnungen konfigurieren, während Sie die Optionen für Ihren Kontext einrichten – in der Regel in DbContext.OnConfiguring, oder wenn Startup.cs Sie ASP.NET Core verwenden.

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