Effiziente AbfragenEfficient Querying

Das effiziente Abfragen ist ein umfassender Artikel, der Themen wie Indizes, zugehörige Entitäts Lade Strategien und viele andere Themen behandelt.Querying efficiently is a vast subject, that covers subjects as wide-ranging as indexes, related entity loading strategies, and many others. In diesem Abschnitt werden einige gängige Themen zum schnelleren Ausführen von Abfragen erläutertThis section details some common themes for making your queries faster, and pitfalls users typically encounter.

Ordnungsgemäße Verwendung von IndizesUse indexes properly

Der wichtigste Entscheidungsfaktor, ob eine Abfrage schnell ausgeführt wird oder nicht, ist, ob Sie ggf. Indizes ordnungsgemäß verwendet: Datenbanken werden normalerweise verwendet, um große Datenmengen zu speichern, und Abfragen, die ganze Tabellen durchlaufen, sind in der Regel Quellen für schwerwiegende Leistungsprobleme.The main deciding factor in whether a query runs fast or not is whether it will properly utilize indexes where appropriate: databases are typically used to hold large amounts of data, and queries which traverse entire tables are typically sources of serious performance issues. Indizierungs Probleme sind nicht leicht zu erkennen, weil es nicht sofort ersichtlich ist, ob eine bestimmte Abfrage einen Index verwendet oder nicht.Indexing issues aren't easy to spot, because it isn't immediately obvious whether a given query will use an index or not. Beispiel:For example:

// Matches on start, so uses an index (on SQL Server)
var posts1 = context.Posts.Where(p => p.Title.StartsWith("A")).ToList();
// Matches on end, so does not use the index
var posts2 = context.Posts.Where(p => p.Title.EndsWith("A")).ToList();

Eine gute Möglichkeit, Indizierungs Probleme zu erkennen, besteht darin, zuerst eine langsame Abfrage zu ermitteln und dann den Abfrageplan über das bevorzugte Tool Ihrer Datenbank zu untersuchen. Weitere Informationen zur Vorgehensweise finden Sie auf der Seite " Leistungs Diagnose ".A good way to spot indexing issues is to first pinpoint a slow query, and then examine its query plan via your database's favorite tool; see the performance diagnosis page for more information on how to do that. Der Abfrageplan zeigt an, ob die Abfrage die gesamte Tabelle durchläuft oder einen Index verwendet.The query plan displays whether the query traverses the entire table, or uses an index.

Als allgemeine Regel gilt: Es gibt keine speziellen EF-Kenntnisse zur Verwendung von Indizes oder zur Diagnose von Leistungsproblemen, die im Zusammenhang mit Ihnen auftreten. Allgemeine Daten Bank Kenntnisse im Zusammenhang mit Indizes sind für EF-Anwendungen ebenso wichtig wie für Anwendungen, die EF nicht verwenden.As a general rule, there isn't any special EF knowledge to using indexes or diagnosing performance issues related to them; general database knowledge related to indexes is just as relevant to EF applications as to applications not using EF. Im folgenden sind einige allgemeine Richtlinien aufgeführt, die bei der Verwendung von Indizes beachtet werden sollten:The following lists some general guidelines to keep in mind when using indexes:

  • Obwohl Indizes Abfragen beschleunigen, verlangsamen Sie auch Updates, da Sie auf dem neuesten Stand gehalten werden müssen.While indexes speed up queries, they also slow down updates since they need to be kept up-to-date. Vermeiden Sie die Definition von Indizes, die nicht benötigt werden, und verwenden Sie ggf. Index Filter , um den Index auf eine Teilmenge der Zeilen einzuschränken, wodurch der Aufwand verringert wird.Avoid defining indexes which aren't needed, and consider using index filters to limit the index to a subset of the rows, thereby reducing this overhead.
  • Zusammengesetzte Indizes können Abfragen beschleunigen, die mehrere Spalten filtern, aber Sie können auch Abfragen beschleunigen, die nicht nach allen Spalten des Indexes Filtern (abhängig von der Reihenfolge).Composite indexes can speed up queries which filter on multiple columns, but they can also speed up queries which don't filter on all the index's columns - depending on ordering. Ein Index für die Spalten a und b beschleunigt z. b. Abfragen, die durch a und b gefiltert werden, sowie Abfragen, die nur durch einen filtern. es werden jedoch keine Abfragen beschleunigt, die nur durch B gefiltert werden.For example, an index on columns A and B speed up queries filtering by A and B, as well as queries filtering only by A, but it does not speed up queries filtering over only by B.
  • Wenn eine Abfrage anhand eines Ausdrucks über eine Spalte (z. b.) filtert price / 2 , kann kein einfacher Index verwendet werden.If a query filters by an expression over a column (e.g. price / 2), a simple index cannot be used. Sie können jedoch eine gespeicherte beibehaltene Spalte für ihren Ausdruck definieren und einen Index dafür erstellen.However, you can define a stored persisted column for your expression, and create an index over that. Einige Datenbanken unterstützen auch Ausdrucks Indizes, die direkt verwendet werden können, um Abfragen zu beschleunigen, die durch einen beliebigen Ausdruck gefiltert werden.Some databases also support expression indexes, which can be directly used to speed up queries filtering by any expression.
  • Mit unterschiedlichen Datenbanken können Indizes auf verschiedene Weise konfiguriert werden, und in vielen Fällen EF Core Anbieter diese über die fließende API verfügbar machen.Different databases allow indexes to be configured in various ways, and in many cases EF Core providers expose these via the Fluent API. Beispielsweise können Sie mit dem SQL Server-Anbieter konfigurieren, ob ein Index gruppiertist, oder den Füllfaktorfestlegen.For example, the SQL Server provider allows you to configure whether an index is clustered, or set its fill factor. Weitere Informationen finden Sie in der Dokumentation des Anbieters.Consult your provider's documentation for more information.

Nur Projekteigenschaften, die Sie benötigenProject only properties you need

Mit EF Core ist es sehr einfach, Entitäts Instanzen abzufragen und diese Instanzen dann im Code zu verwenden.EF Core makes it very easy to query out entity instances, and then use those instances in code. Beim Abfragen von Entitäts Instanzen können jedoch häufig mehr Daten als notwendig aus der Datenbank abgerufen werden.However, querying entity instances can frequently pull back more data than necessary from your database. Beachten Sie Folgendes:Consider the following:

foreach (var blog in context.Blogs)
{
    Console.WriteLine("Blog: " + blog.Url);
}

Obwohl dieser Code nur die-Eigenschaft jedes Blogs benötigt Url , wird die gesamte Blog-Entität abgerufen und nicht benötigte Spalten aus der Datenbank übertragen:Although this code only actually needs each Blog's Url property, the entire Blog entity is fetched, and unneeded columns are transferred from the database:

SELECT [b].[BlogId], [b].[CreationDate], [b].[Name], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]

Dies kann mithilfe von optimiert werden Select , um EF mitzuteilen, welche Spalten zu projizieren sind:This can be optimized by using Select to tell EF which columns to project out:

foreach (var blogName in context.Blogs.Select(b => b.Url))
{
    Console.WriteLine("Blog: " + blogName);
}

Das resultierende SQL ruft nur die benötigten Spalten zurück:The resulting SQL pulls back only the needed columns:

SELECT [b].[Url]
FROM [Blogs] AS [b]

Wenn Sie mehr als eine Spalte projizieren müssen, projizieren Sie die gewünschten Eigenschaften in einen anonymen c#-Typ.If you need to project out more than one column, project out to a C# anonymous type with the properties you want.

Beachten Sie, dass diese Technik für schreibgeschützte Abfragen sehr nützlich ist. es wird jedoch komplizierter, wenn Sie die abgerufenen Blogs Aktualisieren müssen, da die Änderungs Nachverfolgung von EF nur mit Entitäts Instanzen funktioniert.Note that this technique is very useful for read-only queries, but things get more complicated if you need to update the fetched blogs, since EF's change tracking only works with entity instances. Es ist möglich, Aktualisierungen auszuführen, ohne ganze Entitäten zu laden, indem Sie eine geänderte Blog Instanz anfügen und EF mitteilen, welche Eigenschaften geändert wurden, aber das ist eine erweiterte Technik, die sich möglicherweise nicht lohnt.It's possible to perform updates without loading entire entities by attaching a modified Blog instance and telling EF which properties have changed, but that is a more advanced technique that may not be worth it.

Begrenzen der Größe des ResultsetsLimit the resultset size

Standardmäßig gibt eine Abfrage alle Zeilen zurück, die mit Ihren Filtern übereinstimmen:By default, a query returns all rows that matches its filters:

var blogsAll = context.Posts
    .Where(p => p.Title.StartsWith("A"))
    .ToList();

Da die Anzahl der zurückgegebenen Zeilen von den tatsächlichen Daten in der Datenbank abhängt, ist es nicht möglich zu wissen, wie viele Daten aus der Datenbank geladen werden, wie viel Arbeitsspeicher von den Ergebnissen belegt wird und wie viel zusätzliche Auslastung bei der Verarbeitung dieser Ergebnisse generiert wird (z. b. durch Senden an einen Benutzer Browser über das Netzwerk).Since the number of rows returned depends on actual data in your database, it's impossible to know how much data will be loaded from the database, how much memory will be taken up by the results, and how much additional load will be generated when processing these results (e.g. by sending them to a user browser over the network). Entscheidend ist, dass Testdatenbanken häufig wenig Daten enthalten, sodass alles gut funktioniert, wenn die Abfrage in realen Daten ausgeführt wird und viele Zeilen zurückgegeben werden.Crucially, test databases frequently contain little data, so that everything works well while testing, but performance problems suddenly appear when the query starts running on real-world data and many rows are returned.

Daher ist es in der Regel sinnvoll, die Anzahl der Ergebnisse einzuschränken:As a result, it's usually worth giving thought to limiting the number of results:

var blogs25 = context.Posts
    .Where(p => p.Title.StartsWith("A"))
    .Take(25)
    .ToList();

Die Benutzeroberfläche kann mindestens eine Meldung anzeigen, die besagt, dass in der Datenbank mehr Zeilen vorhanden sein können (und Sie auf andere Weise abrufen können).At a minimum, your UI could show a message indicating that more rows may exist in the database (and allow retrieving them in some other manner). Eine vollständige Lösung würde das Paging implementieren, bei dem Ihre Benutzeroberfläche nur eine bestimmte Anzahl von Zeilen gleichzeitig anzeigt und Benutzern ermöglicht, bei Bedarf zur nächsten Seite zu gelangen. in der Regel werden Take die Skip Operatoren und kombiniert, um jedes Mal einen bestimmten Bereich im Resultset auszuwählen.A full-blown solution would implement paging, where your UI only shows a certain number of rows at a time, and allow users to advance to the next page as needed; this typically combines the Take and Skip operators to select a specific range in the resultset each time.

In relationalen Datenbanken werden alle zugehörigen Entitäten standardmäßig durch Einführung von JOIN-Vorgängen in eine Einzelabfrage geladen.In relational databases, all related entities are loaded by introducing JOINs in single query.

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]

Wenn ein typischer Blog mehrere zugehörige Beiträge enthält, werden in den Zeilen für diese Beiträge die Informationen des Blogs dupliziert.If a typical blog has multiple related posts, rows for these posts will duplicate the blog's information. Diese Duplizierung führt zum so genannten Problem der „kartesischen Explosion“.This duplication leads to the so-called "cartesian explosion" problem. Wenn weitere 1:n-Beziehungen geladen werden, wächst die Menge an duplizierten Daten weiter und beeinträchtigt die Leistung Ihrer Anwendung.As more one-to-many relationships are loaded, the amount of duplicated data may grow and adversely affect the performance of your application.

EF ermöglicht die Vermeidung dieses Effekts durch die Verwendung von "Split Queries", die die verknüpften Entitäten über separate Abfragen laden.EF allows avoiding this effect via the use of "split queries", which load the related entities via separate queries. Weitere Informationen finden Sie in der Dokumentation zu Split und Single Queries.For more information, read the documentation on split and single queries.

Hinweis

Die aktuelle Implementierung von Split-Abfragen führt einen Roundtrip für jede Abfrage aus.The current implementation of split queries executes a roundtrip for each query. Wir planen, dies in Zukunft zu verbessern und alle Abfragen in einem einzigen Roundtrip auszuführen.We plan to improve this in the future, and execute all queries in a single roundtrip.

Es wird empfohlen, die dedizierte Seite für Verwandte Entitäten zu lesen, bevor Sie mit diesem Abschnitt fortfahren.It's recommended to read the dedicated page on related entities before continuing with this section.

Beim Umgang mit verwandten Entitäten wissen wir in der Regel, was wir laden müssen: ein typisches Beispiel wäre das Laden einer bestimmten Gruppe von Blogs zusammen mit allen Beiträgen.When dealing with related entities, we usually know in advance what we need to load: a typical example would be loading a certain set of Blogs, along with all their Posts. In diesen Szenarien ist es immer besser, Eager Loadingzu verwenden, damit EF alle erforderlichen Daten in einem Roundtrip abrufen kann.In these scenarios, it is always better to use eager loading, so that EF can fetch all the required data in one roundtrip. Das gefilterte include -Feature, das in EF Core 5,0 eingeführt wurde, ermöglicht Ihnen außerdem, zu begrenzen, welche verknüpften Entitäten Sie laden möchten, während der Ladeprozess in einem einzigen Roundtrip erwartungsgemäß bleibt:The filtered include feature, introduced in EF Core 5.0, also allows you to limit which related entities you'd like to load, while keeping the loading process eager and therefore doable in a single roundtrip:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

In anderen Szenarien wissen wir möglicherweise nicht, welche verwandte Entität wir benötigen, bevor wir die Prinzipal Entität erhalten.In other scenarios, we may not know which related entity we're going to need before we get its principal entity. Wenn Sie z. b. einen Blog laden, müssen wir uns möglicherweise eine andere Datenquelle (möglicherweise einen Webdienst) ansehen, um zu erfahren, ob wir an den Beiträgen dieses Blogs interessiert sind.For example, when loading some Blog, we may need to consult some other data source - possibly a webservice - in order to know whether we're interested in that Blog's Posts. In diesen Fällen kann explizites oder Lazy Load verwendet werden, um verknüpfte Entitäten separat abzurufen und die Post-Navigation des Blogs aufzufüllen.In these cases, explicit or lazy loading can be used to fetch related entities separately, and populate the Blog's Posts navigation. Beachten Sie, dass, da diese Methoden nicht eifrig sind, zusätzliche Roundtrips zur Datenbank erforderlich sind, d. h. die Quelle der Verlangsamung. abhängig von Ihrem speziellen Szenario ist es möglicherweise effizienter, alle Beiträge immer zu laden, anstatt die zusätzlichen Roundtrips auszuführen und selektiv nur die benötigten Beiträge zu erhalten.Note that since these methods aren't eager, they require additional roundtrips to the database, which is source of slowdown; depending on your specific scenario, it may be more efficient to just always load all Posts, rather than to execute the additional roundtrips and selectively get only the Posts you need.

Vorsicht Lazy LoadingBeware of lazy loading

Lazy Load scheint eine sehr nützliche Methode zum Schreiben von Daten Bank Logik zu sein, da EF Core automatisch verknüpfte Entitäten aus der Datenbank lädt, wenn der Code auf Sie zugreift.Lazy loading often seems like a very useful way to write database logic, since EF Core automatically loads related entities from the database as they are accessed by your code. Dadurch wird vermieden, dass verknüpfte Entitäten geladen werden, die nicht benötigt werden (z. b. Explizites Laden)This avoids loading related entities that aren't needed (like explicit loading), and seemingly frees the programmer from having to deal with related entities altogether. Lazy Loading ist jedoch besonders anfällig für die Erstellung nicht benötigter zusätzlicher Roundtrips, die die Anwendung verlangsamen können.However, lazy loading is particularly prone for producing unneeded extra roundtrips which can slow the application.

Beachten Sie Folgendes:Consider the following:

foreach (var blog in context.Blogs.ToList())
{
    foreach (var post in blog.Posts)
    {
        Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
    }
}

Dieser scheinbar unschuldige Code Abschnitt durchläuft alle Blogs und ihre Beiträge und druckt sie. Beim Aktivieren der Anweisungs Protokollierung von EF Core wird Folgendes angezeigt:This seemingly innocent piece of code iterates through all the blogs and their posts, printing them out. Turning on EF Core's statement logging reveals the following:

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

Was geht da vor?What's going on here? Warum werden all diese Abfragen für die oben genannten einfachen Schleifen gesendet?Why are all these queries being sent for the simple loops above? Bei Lazy Loading werden die Beiträge eines Blogs nur (verzögert) geladen, wenn auf seine Posts-Eigenschaft zugegriffen wird. Folglich löst jede Iterationen im Inneren foreach eine zusätzliche Datenbankabfrage in einem eigenen Roundtrip aus.With lazy loading, a Blog's Posts are only (lazily) loaded when its Posts property is accessed; as a result, each iteration in the inner foreach triggers an additional database query, in its own roundtrip. Nachdem die erste Abfrage alle Blogs geladen hat, haben wir daher eine weitere Abfrage pro Blog, die alle Beiträge lädt. Dies wird manchmal als " N + 1 "-Problem bezeichnet und kann zu erheblichen Leistungsproblemen führen.As a result, after the initial query loading all the blogs, we then have another query per blog, loading all its posts; this is sometimes called the N+1 problem, and it can cause very significant performance issues.

Angenommen, wir benötigen alle Blogbeiträge, aber es ist sinnvoll, stattdessen Eager Loading zu verwenden.Assuming we're going to need all of the blogs' posts, it makes sense to use eager loading here instead. Wir können den include -Operator verwenden, um das Laden auszuführen, aber da wir nur die URLs der Blogs benötigen (und nur die benötigten Elemente ladensollten).We can use the Include operator to perform the loading, but since we only need the Blogs' URLs (and we should only load what's needed). Wir verwenden stattdessen eine Projektion:So we'll use a projection instead:

foreach (var blog in context.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
{
    foreach (var post in blog.Posts)
    {
        Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
    }
}

Dadurch wird EF Core alle Blogs zusammen mit ihren Beiträgen in einer einzigen Abfrage abrufen können.This will make EF Core fetch all the Blogs - along with their Posts - in a single query. In einigen Fällen kann es auch hilfreich sein, kartesische Explosions Effekte mithilfe von Split- Abfragenzu vermeiden.In some cases, it may also be useful to avoid cartesian explosion effects by using split queries.

Warnung

Da Lazy Loading das Auftreten des Problems mit N + 1 äußerst einfach machen kann, empfiehlt es sich, dies zu vermeiden.Because lazy loading makes it extremely easy to inadvertently trigger the N+1 problem, it is recommended to avoid it. Ein eifriges oder explizites Laden macht es im Quellcode ganz klar, wenn ein datenbankroundtrip auftritt.Eager or explicit loading make it very clear in the source code when a database roundtrip occurs.

Pufferung und StreamingBuffering and streaming

Die Pufferung bezieht sich auf das Laden aller Abfrageergebnisse in den Arbeitsspeicher, während Streaming bedeutet, dass EF die Anwendung jedes Mal ein einzelnes Ergebnis übergibt und nie das gesamte Resultset im Arbeitsspeicher enthält.Buffering refers to loading all your query results into memory, whereas streaming means that EF hands the application a single result each time, never containing the entire resultset in memory. Im Prinzip werden die Arbeitsspeicher Anforderungen einer streaminganfrage korrigiert. Sie sind identisch, unabhängig davon, ob die Abfrage eine Zeile oder 1000 zurückgibt. eine Puffer Abfrage hingegen erfordert mehr Arbeitsspeicher, je mehr Zeilen zurückgegeben werden.In principle, the memory requirements of a streaming query are fixed - they are the same whether the query returns 1 row or 1000; a buffering query, on the other hand, requires more memory the more rows are returned. Bei Abfragen, die umfangreiche Resultsets ergeben, kann dies ein wichtiger Leistungsfaktor sein.For queries that result large resultsets, this can be an important performance factor.

Ob ein Abfrage Puffer oder Datenströme von der Auswertung abhängig sind:Whether a query buffers or streams depends on how it is evaluated:

// ToList and ToArray cause the entire resultset to be buffered:
var blogsList = context.Posts.Where(p => p.Title.StartsWith("A")).ToList();
var blogsArray = context.Posts.Where(p => p.Title.StartsWith("A")).ToArray();

// Foreach streams, processing one row at a time:
foreach (var blog in context.Posts.Where(p => p.Title.StartsWith("A")))
{
    // ...
}

// AsEnumerable 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
    .AsEnumerable()
    .Where(p => SomeDotNetMethod(p)); // Executed at the client on all database results

Wenn Ihre Abfragen nur ein paar Ergebnisse zurückgeben, müssen Sie sich nicht darum kümmern.If your queries return just a few results, then you probably don't have to worry about this. Wenn die Abfrage jedoch möglicherweise eine große Anzahl von Zeilen zurückgibt, ist es sinnvoll, das Streaming anstelle von Pufferung zu überdenken.However, if your query might return large numbers of rows, it's worth giving thought to streaming instead of buffering.

Hinweis

Vermeiden ToList ToArray Sie die Verwendung von oder, wenn Sie einen anderen LINQ-Operator für das Ergebnis verwenden möchten. Dadurch werden alle Ergebnisse unnötig in den Arbeitsspeicher gepuffert.Avoid using ToList or ToArray if you intend to use another LINQ operator on the result - this will needlessly buffer all results into memory. Verwenden Sie stattdessen AsEnumerable.Use AsEnumerable instead.

Interne Pufferung von EFInternal buffering by EF

In bestimmten Situationen wird das Resultset unabhängig davon, wie Sie die Abfrage auswerten, von EF intern gepuffert.In certain situations, EF will itself buffer the resultset internally, regardless of how you evaluate your query. Dies sind die beiden Fälle, in denen dies geschieht:The two cases where this happens are:

  • Wenn eine Wiederholungs Ausführungs Strategie vorhanden ist.When a retrying execution strategy is in place. Dadurch wird sichergestellt, dass die gleichen Ergebnisse zurückgegeben werden, wenn die Abfrage später wiederholt wird.This is done to make sure the same results are returned if the query is retried later.
  • Wenn Split Query verwendet wird, werden die Resultsets aller außer der letzten Abfrage gepuffert, es sei denn, Mars ist für SQL Server aktiviert.When split query is used, the resultsets of all but the last query are buffered - unless MARS is enabled on SQL Server. Dies liegt daran, dass es in der Regel nicht möglich ist, mehrere Resultsets für Abfragen gleichzeitig zu aktivieren.This is because it is usually impossible to have multiple query resultsets active at the same time.

Beachten Sie, dass diese interne Pufferung zusätzlich zu sämtlicher Pufferung erfolgt, die Sie über LINQ-Operatoren auslösen.Note that this internal buffering occurs in addition to any buffering you cause via LINQ operators. Wenn Sie z. b. für ToList eine Abfrage verwenden und eine Wiederholungs Ausführungs Strategie vorhanden ist, wird das Resultset zweimal in den Arbeitsspeicher geladen: einmal intern von EF und einmal von ToList .For example, if you use ToList on a query and a retrying execution strategy is in place, the resultset is loaded into memory twice: once internally by EF, and once by ToList.

Nachverfolgung, keine Nachverfolgung und Identitäts AuflösungTracking, no-tracking and identity resolution

Es wird empfohlen, die dedizierte Seite zur Überwachung und ohne Nachverfolgung zu lesen, bevor Sie mit diesem Abschnitt fortfahren.It's recommended to read the dedicated page on tracking and no-tracking before continuing with this section.

EF verfolgt standardmäßig Entitäts Instanzen, sodass Änderungen an den Instanzen erkannt und persistent gespeichert werden, wenn SaveChanges aufgerufen wird.EF tracks entity instances by default, so that changes on them are detected and persisted when SaveChanges is called. Ein weiterer Effekt bei der Nachverfolgung von Abfragen besteht darin, dass EF erkennt, ob bereits eine Instanz für Ihre Daten geladen wurde, und dass diese überwachte Instanz automatisch zurückgegeben wird, anstatt eine neue Instanz zurückzugeben. Dies wird als Identitäts Auflösung bezeichnet.Another effect of tracking queries is that EF detects if an instance has already been loaded for your data, and will automatically return that tracked instance rather than returning a new one; this is called identity resolution. Im Hinblick auf die Leistung bedeutet die Änderungs Nachverfolgung Folgendes:From a performance perspective, change tracking means the following:

  • EF verwaltet intern ein Wörterbuch mit nach verfolgten Instanzen.EF internally maintains a dictionary of tracked instances. Wenn neue Daten geladen werden, prüft EF das Wörterbuch, um festzustellen, ob bereits eine Instanz für den Schlüssel der Entität (Identitäts Auflösung) nachverfolgt wird.When new data is loaded, EF checks the dictionary to see if an instance is already tracked for that entity's key (identity resolution). Die Wörterbuch Wartung und Suchvorgänge nehmen einige Zeit in Anspruch, wenn die Ergebnisse der Abfrage geladen werden.The dictionary maintenance and lookups take up some time when loading the query's results.
  • Bevor eine geladene Instanz an die Anwendung übergeben wird, erstellt EF diese Instanz und speichert die Momentaufnahme intern.Before handing a loaded instance to the application, EF snapshots that instance and keeps the snapshot internally. Wenn SaveChanges aufgerufen wird, wird die Instanz der Anwendung mit der Momentaufnahme verglichen, um die Änderungen zu ermitteln, die persistent gespeichert werden sollen.When SaveChanges is called, the application's instance is compared with the snapshot to discover the changes to be persisted. Die Momentaufnahme benötigt mehr Arbeitsspeicher, und der Snapshotprozess selbst nimmt Zeit in Rechnung. Manchmal ist es möglich, ein anderes, möglicherweise effizienteres snapshotverhalten über Wert Vergleicheanzugeben oder Proxys für die Änderungs Nachverfolgung zu verwenden, um den Snapshotprozess vollständig zu umgehen (Dies ist jedoch mit einem eigenen Satz von Nachteilen verbunden).The snapshot takes up more memory, and the snapshotting process itself takes time; it's sometimes possible to specify different, possibly more efficient snapshotting behavior via value comparers, or to use change-tracking proxies to bypass the snapshotting process altogether (though that comes with its own set of disadvantages).

In schreibgeschützten Szenarien, in denen Änderungen nicht in der Datenbank gespeichert werden, können die oben genannten Aufwand mithilfe von Abfragen ohne Nachverfolgungvermieden werden.In read-only scenarios where changes aren't saved back to the database, the above overheads can be avoided by using no-tracking queries. Da keine nach Verfolgungs Abfragen jedoch keine Identitäts Auflösung durchführen, wird eine Datenbankzeile, auf die von mehreren anderen geladenen Zeilen verwiesen wird, als unterschiedliche Instanzen materialisiert.However, since no-tracking queries do not perform identity resolution, a database row which is referenced by multiple other loaded rows will be materialized as as different instances.

Um dies zu veranschaulichen, nehmen wir an, dass wir eine große Anzahl von Beiträgen aus der Datenbank laden, ebenso wie der Blog, auf den jeder Beitrag verweist.To illustrate, assume we are loading a large number of Posts from the database, as well as the Blog referenced by each Post. Wenn 100 Beiträge auf denselben Blog verweisen, erkennt eine Überwachungs Abfrage dies über die Identitäts Auflösung, und alle Post-Instanzen verweisen auf dieselbe deduplizierte Blog Instanz.If 100 Posts happen to reference the same Blog, a tracking query detects this via identity resolution, and all Post instances will refer the same de-duplicated Blog instance. Eine Abfrage ohne Nachverfolgung hingegen dupliziert denselben Blog 100-Mal, und der Anwendungscode muss entsprechend geschrieben werden.A no-tracking query, in contrast, duplicates the same Blog 100 times - and application code must be written accordingly.

Im folgenden finden Sie die Ergebnisse für einen Vergleichstest zum Vergleichen von Nachverfolgung und ohne Nachverfolgung für eine Abfrage, bei der 10 Blogs mit 20 Beiträgen geladen werden.Here are the results for a benchmark comparing tracking vs. no-tracking behavior for a query loading 10 Blogs with 20 Posts each. Der Quellcode ist hier verfügbar. Sie können ihn als Grundlage für Ihre eigenen Messungen verwenden.The source code is available here, feel free to use it as a basis for your own measurements.

MethodeMethod NumblogsNumBlogs NumpostsperblogNumPostsPerBlog MittelwertMean FehlerError StdDevStdDev MedianMedian SeitenverhältnisRatio RatiosdRatioSD Gen 0Gen 0 Gen 1Gen 1 Gen 2Gen 2 ZugeordnetAllocated
AstrackingAsTracking 1010 2020 1.414,7 US-1,414.7 us 27,20 US-27.20 us 45,44 US-45.44 us 1.405,5 US-1,405.5 us 1.001.00 0.000.00 60,546960.5469 13,671913.6719 - 380,11 KB380.11 KB
AsNoTrackingAsNoTracking 1010 2020 993,3 US-993.3 us 24,04 US-24.04 us 65,40 US-65.40 us 966,2 US-966.2 us 0.710.71 0.050.05 37,109437.1094 6,83596.8359 - 232,89 KB232.89 KB

Schließlich ist es möglich, Updates ohne den Aufwand der Änderungs Nachverfolgung durchzuführen, indem eine Abfrage ohne Nachverfolgung genutzt und dann die zurückgegebene Instanz an den Kontext angefügt wird. dabei wird angegeben, welche Änderungen vorgenommen werden sollen.Finally, it is possible to perform updates without the overhead of change tracking, by utilizing a no-tracking query and then attaching the returned instance to the context, specifying which changes are to be made. Dies überträgt die Last der Änderungs Nachverfolgung von EF auf den Benutzer und sollte nur dann versucht werden, wenn der mehr Aufwand für die Änderungs Nachverfolgung durch Profilerstellung oder Benchmarktests als nicht akzeptabel angezeigt wird.This transfers the burden of change tracking from EF to the user, and should only be attempted if the change tracking overhead has been shown to be unacceptable via profiling or benchmarking.

Verwenden von RAW SQLUsing raw SQL

In einigen Fällen ist eine optimierte SQL-Datei für die Abfrage vorhanden, die EF nicht generiert.In some cases, more optimized SQL exists for your query, which EF does not generate. Dies kann der Fall sein, wenn das SQL-Konstrukt eine Erweiterung für Ihre Datenbank ist, die nicht unterstützt wird, oder nur, weil EF noch nicht in die Datenbank übersetzt wird.This can happen when the SQL construct is an extension specific to your database that's unsupported, or simply because EF does not translate to it yet. In diesen Fällen kann das Schreiben von SQL durch Hand eine beträchtliche Leistungssteigerung bieten, und EF unterstützt mehrere Möglichkeiten.In these cases, writing SQL by hand can provide a substantial performance boost, and EF supports several ways to do this.

  • Verwenden Sie Rohdaten von SQL direkt in der Abfrage, z. b. über FromSqlRaw .Use raw SQL directly in your query, e.g. via FromSqlRaw. EF ermöglicht Ihnen sogar das Verfassen von SQL-Rohdaten mit regulären LINQ-Abfragen, sodass Sie nur einen Teil der Abfrage in unformatierten SQL-Daten Ausdrücken können.EF even lets you compose over the raw SQL with regular LINQ queries, allowing you to express only a part of the query in raw SQL. Dies ist eine gute Methode, wenn die unformatierte SQL-Datei nur in einer einzelnen Abfrage in ihrer CodeBase verwendet werden muss.This is a good technique when the raw SQL only needs to be used in a single query in your codebase.
  • Definieren Sie eine benutzerdefinierte Funktion (User-Defined Function , UDF), und nennen Sie Sie dann in Ihren Abfragen.Define a user-defined function (UDF), and then call that from your queries. Beachten Sie, dass EF seit 5,0 das Zurückgeben von vollständigen Resultsets ermöglicht. Diese werden als Tabellenwert Funktionen (Table-Wert Functions, TVFs) bezeichnet und ermöglichen außerdem die Zuordnung einer DbSet zu einer Funktion, sodass Sie genau wie in einer anderen Tabelle aussieht.Note that since 5.0, EF allows UDFs to return full resultsets - these are known as table-valued functions (TVFs) - and also allows mapping a DbSet to a function, making it look just like just another table.
  • Definieren Sie eine Daten Bank Sicht, und Fragen Sie Sie in Ihren Abfragen ab.Define a database view and query from it in your queries. Beachten Sie, dass Sichten im Gegensatz zu Funktionen keine Parameter annehmen können.Note that unlike functions, views cannot accept parameters.

Hinweis

Unformatierte SQL-Daten sollten in der Regel als letztes Mittel verwendet werden, nachdem sichergestellt wurde, dass EF das gewünschte SQL nicht generieren kann, und wenn die Leistung für die jeweilige Abfrage recht wichtig ist.Raw SQL should generally be used as a last resort, after making sure that EF can't generate the SQL you want, and when performance is important enough for the given query to justify it. Die Verwendung von RAW SQL bringt erhebliche Wartungs Nachteile mit sich.Using raw SQL brings considerable maintenance disadvantages.

Asynchrone ProgrammierungAsynchronous programming

Als allgemeine Regel gilt: Wenn die Anwendung skalierbar sein soll, ist es wichtig, immer asynchrone APIs anstelle von synchronen APIs zu verwenden (z. b. SaveChangesAsync anstelle von SaveChanges ).As a general rule, in order for your application to be scalable, it's important to always use asynchronous APIs rather than synchronous one (e.g. SaveChangesAsync rather than SaveChanges). Synchrone APIs blockieren den Thread für die Dauer der Datenbank-e/a, wodurch die Notwendigkeit von Threads und die Anzahl von Thread Kontext Schaltern erhöht werden muss.Synchronous APIs block the thread for the duration of database I/O, increasing the need for threads and the number of thread context switches that must occur.

Weitere Informationen finden Sie auf der Seite zu Async-Programmierung.For more information, see the page on async programming.

Warnung

Vermeiden Sie das Kombinieren von synchronem und asynchronem Code in derselben Anwendung. es ist sehr einfach, Probleme mit einem geringfügigen Thread Pool zu beheben.Avoid mixing synchronous and asynchronous code in the same application - it's very easy to inadvertently trigger subtle thread-pool starvation issues.

Zusätzliche RessourcenAdditional resources

Im Abschnitt Leistung der Seite mit der NULL-Vergleichs Dokumentation finden Sie einige bewährte Methoden beim Vergleich von Werten, die NULL-Werte zulassen.See the performance section of the null comparison documentation page for some best practices when comparing nullable values.