Überlegungen zur Leistung (Entity Framework)

In diesem Thema werden Leistungsmerkmale des ADO.NET Entity Framework beschrieben. Außerdem sind einige Vorschläge enthalten, die Sie zur Verbesserung der Leistung von Entity Framework-Anwendungen verwenden können.

Phasen der Abfrageausführung

Zum besseren Verständnis der Abfrageleistung im Entity Framework ist es hilfreich zu verstehen, welche Vorgänge ablaufen, wenn eine Abfrage für ein konzeptionelles Modell ausgeführt wird und dabei Daten als Objekte zurückgegeben werden. Die folgende Tabelle beschreibt diese Reihe von Vorgängen.

Vorgang Relative Kosten Häufigkeit Kommentare
Laden von Metadaten Moderat Einmal pro Anwendungsdomäne. Von Entity Framework verwendete Modell- und Zuordnungsmetadaten werden in eine MetadataWorkspace geladen. Diese Metadaten werden global zwischengespeichert und stehen für weitere Instanzen von ObjectContext in der gleichen Anwendungsdomäne zur Verfügung.
Öffnen der Datenbankverbindung Moderat1 Nach Bedarf. Da durch eine offene Verbindung zur Datenbank wertvolle Ressourcen verbraucht werden, öffnet und schließt das Entity Framework die Datenbankverbindung nur bei Bedarf. Sie können die Verbindung auch explizit öffnen. Weitere Informationen finden Sie unter Verwalten von Verbindungen und Transaktionen.
Generieren von Sichten High Einmal pro Anwendungsdomäne. (Kann zuvor generiert werden.) Bevor von Entity Framework für ein konzeptionelles Modell eine Abfrage ausgeführt oder Änderungen an der Datenquelle gespeichert werden können, muss ein Satz lokaler Abfrageansichten für den Zugriff auf die Datenbank generiert werden. Aufgrund der hohen Kosten für die Generierung dieser Ansichten können Sie die Sichten zuvor generieren und dem Projekt zur Entwurfszeit hinzufügen. Weitere Informationen finden Sie unter Gewusst wie: Vorgenerieren von Ansichten zur Verbesserung der Abfrageleistung.
Vorbereiten der Abfrage Moderat2 Einmal für jede eindeutige Abfrage. Schließt die Kosten für das Verfassen des Abfragebefehls, die Erstellung einer Befehlsstruktur auf Grundlage von Modell- und Zuordnungsmetadaten und die Definition der Form der zurückgegebenen Daten ein. Da jetzt sowohl Entity SQL-Abfragebefehle als auch LINQ-Abfragen zwischengespeichert werden, ist für spätere Ausführungen derselben Abfrage weniger Zeit erforderlich. Sie können weiterhin kompilierte LINQ-Abfragen verwenden, um diesen Aufwand in späteren Ausführungen zu reduzieren. Kompilierte Abfragen können effizienter als LINQ-Abfragen sein, die automatisch zwischengespeichert werden. Weitere Informationen Sie unter Kompilierte Abfragen (LINQ to Entities). Allgemeine Informationen zur Ausführung von LINQ-Abfragen finden Sie unter LINQ to Entities. Hinweis: LINQ to Entities-Abfragen, die den Enumerable.Contains-Operator auf speicherresidente Auflistungen anwenden, werden nicht automatisch zwischengespeichert. Darüber hinaus ist das Parametrisieren von Auflistungen im Arbeitsspeicher in kompilierten LINQ-Abfragen nicht zulässig.
Ausführen der Abfrage Niedrig2 Einmal für jede Abfrage. Die Kosten für das Ausführen des Befehls für die Datenquelle mithilfe des ADO.NET-Datenanbieters. Da von den meisten Datenquellen Abfragepläne zwischengespeichert werden, kann die gleiche Abfrage ggf. noch schneller ausgeführt werden.
Laden und Überprüfen von Typen Niedrig3 Einmal für jede ObjectContext-Instanz. Typen werden geladen und gegen die Typen geprüft, die das konzeptionelle Modell definiert.
Nachverfolgung Niedrig3 Einmal für jedes Objekt, dass von einer Abfrage zurückgegeben wird. 4 Verwendet eine Abfrage die NoTracking-Mergeoption, wird die Leistung in dieser Phase nicht beeinträchtigt.

Verwendet die Abfrage die AppendOnly-, PreserveChanges- oder OverwriteChanges-Mergeoption, werden Abfrageergebnisse in der ObjectStateManager nachverfolgt. Für jedes von der Abfrage zurückgegebene verfolgte Objekt wird ein EntityKey generiert, das für die Erstellung eines ObjectStateEntry im ObjectStateManager verwendet wird. Wenn ein ObjectStateEntry für den EntityKey vorhanden ist, wird das vorhandene Objekt zurückgegeben. Wir die PreserveChanges- oder OverwriteChanges-Option verwendet, wird das Objekt vor der Rückgabe aktualisiert.

Weitere Informationen finden Sie unter Identitätsauflösung, Statusverwaltung und Änderungsnachverfolgung.
Materialisieren der Objekte Moderat3 Einmal für jedes Objekt, dass von einer Abfrage zurückgegeben wird. 4 Der Prozess des Lesens des zurückgegebenen DbDataReader-Objekts, des Erstellens von Objekten und des Festlegens von Eigenschaftswerten, die auf den Werten in jeder Instanz der DbDataRecord-Klasse basieren. Ist das Objekt bereits im ObjectContext vorhanden und wird von der Abfrage die AppendOnly- oder PreserveChanges-Zusammenführungsoptionen verwendet, wird von dieser Phase die Leistung nicht beeinträchtigt. Weitere Informationen finden Sie unter Identitätsauflösung, Statusverwaltung und Änderungsnachverfolgung.

1 Implementiert ein Datenquellenanbieter Verbindungspooling, werden die Kosten für das Öffnen einer Verbindung im Pool verteilt. Der .NET-Anbieter für SQL Server unterstützt Verbindungspooling.

2 Die Kosten steigen mit der Abfragekomplexität.

3 Die Gesamtkosten steigen proportional zur Anzahl der von der Abfrage zurückgegebenen Objekte.

4 Diese Kosten sind für EntityClient-Abfragen nicht erforderlich, da EntityClient-Abfragen anstelle von Objekten einen EntityDataReader zurückgeben. Weitere Informationen finden Sie unter EntityClient-Anbieter für das Entity Framework.

Weitere Überlegungen

Die im Folgenden beschriebenen Vorgehensweisen wirken sich möglicherweise auf die Leistung von Entity Framework-Anwendungen aus.

Abfrageausführung

Da Abfragen ressourcenintensiv sein können, muss berücksichtigt werden, an welcher Stelle im Code und auf welchem Computer eine Abfrage ausgeführt wird.

Verzögerte und unmittelbare Ausführung

Bei der Erstellung einer ObjectQuery<T>- oder einer LINQ-Abfrage wird die Abfrage möglicherweise nicht sofort ausgeführt. Die Abfrageausführung wird so lange verzögert, bis die Ergebnisse benötigt werden, z. B. während einer foreach (C#)-Enumeration, einer For Each (Visual Basic)-Enumeration oder wenn eine List<T>-Auflistung ausgefüllt werden soll. Die Abfrage wird ausgeführt, wenn Sie die Execute-Methode für ein ObjectQuery<T> aufrufen oder wenn Sie eine LINQ-Methode aufrufen, die eine Singleton-Abfrage zurückgibt, z. B. First oder Any. Weitere Informationen finden Sie unter Objektabfragen und Ausführen von Abfragen (LINQ to Entities).

Clientseitige Ausführung von LINQ-Abfragen

Obwohl eine LINQ-Abfrage auf dem Computer ausgeführt wird, der die Datenquelle hostet, werden einige Teile der LINQ-Abfrage möglicherweise auf dem Clientcomputer ausgewertet. Weitere Informationen finden Sie im Abschnitt zur Speicherausführung unter Ausführen von Abfragen (LINQ to Entities).

Abfrage- und Zuordnungskomplexität

Die Komplexität einzelner Abfragen und der Zuordnung im Entitätsmodell wirkt sich entscheidend auf die Abfrageleistung aus.

Zuordnungskomplexität

Modelle, die komplexer als eine einfache 1:1-Zuordnung für Entitäten im konzeptionellen Modell und Tabellen im Speichermodell sind, generieren komplexere Befehle als Modelle mit 1:1-Zuordnung.

Abfragekomplexität

Abfragen, die eine große Anzahl von Joins für Befehle erfordern, die für die Datenquelle ausgeführt werden oder eine große Datenmenge zurückgeben, beeinträchtigen möglicherweise die Leistung auf die folgende Weise:

  • Scheinbar einfache Abfragen für ein konzeptionelles Modell führen möglicherweise zur Ausführung komplexerer Abfragen für die Datenquelle. Dies kann auftreten, da Entity Framework eine Abfrage für ein konzeptionelles Modell in eine entsprechende Abfrage für die Datenquelle übersetzt. Wenn ein einzelner Entitätssatz im konzeptionellen Modell mehr als einer Tabelle in der Datenquelle zugeordnet wird oder einer Jointabelle eine Beziehung zwischen Entitäten zugeordnet wird, sind für den Abfragebefehl für die Datenquellenabfrage möglicherweise Joins erforderlich.

    Hinweis

    Verwenden Sie die ToTraceString-Methode der ObjectQuery<T>- oder EntityCommand-Klasse, um die Befehle anzuzeigen, die für die Datenquelle für eine angegebene Abfrage ausgeführt werden. Weitere Informationen finden Sie unter Vorgehensweise: Anzeigen der Store-Befehle.

  • Geschachtelte Entity SQL-Abfragen erstellen möglicherweise Joins auf dem Server und geben eine große Anzahl von Zeilen zurück.

    Im Folgenden sehen Sie ein Beispiel für eine geschachtelte Abfrage in einer Projektionsklausel:

    SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c  ) As Inner2
        FROM AdventureWorksModel.JobCandidate AS c  ) As Inner1
        FROM AdventureWorksModel.EmployeeDepartmentHistory AS c  
    

    Außerdem bewirken solche Abfragen, dass die Abfragepipeline eine einzelne Abfrage durch die Verdoppelung von Objekten in geschachtelten Abfragen generiert. Deswegen wird eine einzelne Spalte möglicherweise mehrmals dupliziert. In einigen Datenbanken, einschließlich SQL Server, kann hierdurch die TempDB-Tabelle stark vergrößert werden, wodurch die Serverleistung abnehmen kann. Bei der Ausführung geschachtelter Abfragen sollte also vorsichtig vorgegangen werden.

  • Alle Abfragen, die eine große Datenmenge zurückgeben, können einen Leistungsabfall verursachen, wenn vom Client Operationen ausgeführt werden, von denen Ressourcen in der Größenordnung des Resultsets verbraucht werden. In solchen Fällen sollten Sie erwägen, die von der Abfrage zurückgegebene Datenmenge zu beschränken. Weitere Informationen finden Sie unter Vorgehensweise: Blättern durch Abfrageergebnisse.

Alle automatisch vom Entity Framework generierten Befehle sind möglicherweise komplexer als ähnliche explizit von einem Datenbankentwickler geschriebenen Befehle. Wenn Sie explizite Kontrolle über die für die Datenquelle ausgeführten Befehle benötigen, erwägen Sie die Definition einer Zuordnung für eine Tabellenwertfunktion oder eine gespeicherte Prozedur.

Beziehungen

Für optimale Abfrageleistung müssen Beziehungen zwischen Entitäten als Zuordnungen im Entitätsmodell und als logische Beziehungen in der Datenquelle definiert werden.

Abfragepfade

Standardmäßig werden verknüpfte Objekte nicht zurückgegeben (obwohl dies für Objekte, die die Beziehungen selbst darstellen, zutrifft), wenn Sie einen ObjectQuery<T> ausführen. Sie können auf eine von drei Arten verknüpfte Objekte laden:

  1. Legen Sie den Abfragepfad fest, bevor die ObjectQuery<T>-Abfrage ausgeführt wird.

  2. Rufen Sie die Load-Methode für die Navigationseigenschaft auf, die das Objekt verfügbar macht.

  3. Legen Sie die LazyLoadingEnabled-Option für das ObjectContext-Objekt auf true fest. Beachten Sie, dass dies automatisch geschieht, wenn Sie mit dem Entity Data Model Designer Code auf Objektebene generieren. Weitere Informationen finden Sie unter Übersicht über generierten Code.

Denken Sie beim Auswählen der Option daran, dass zwischen der Anzahl der Abfragen der Datenbank und der in einer einzelnen Abfrage zurückgegebenen Datenmenge abgewogen werden sollte. Weitere Informationen finden Sie unter Laden von verknüpften Objekten.

Verwenden von Abfragepfaden

Abfragepfade definieren das Diagramm von Objekten, die von einer Abfrage zurückgegeben werden. Beim Definieren eines Abfragepfads ist nur eine einzige Abfrage der Datenbank erforderlich, um alle durch den Pfad definierten Objekte zurückzugeben. Durch die Verwendung von Abfragepfaden können aus scheinbar einfachen Objektabfragen komplexe Befehle werden, die in der Datenquelle ausgeführt werden. Der Grund hierfür ist, dass eine oder mehrere Joins erforderlich sind, um verbundene Objekte in einer einzelnen Abfrage zurückzugeben. Diese Komplexität nimmt bei Abfragen für ein komplexes Entitätsmodell (z. B. eine Entität mit Vererbung oder ein Pfad, der n:n-Beziehungen enthält) zu.

Hinweis

Mit der ToTraceString-Methode kann der von einer ObjectQuery<T> generierte Befehl angezeigt werden. Weitere Informationen finden Sie unter Vorgehensweise: Anzeigen der Store-Befehle.

Wenn ein Abfragepfad zu viele verbundene Objekte enthält oder die Objekte zu viele Zeilendaten enthalten, kann die Abfrage möglicherweise nicht von der Datenquelle abgeschlossen werden. Dies tritt auf, wenn die Abfrage temporäre Zwischenspeicherung erfordert, die die Kapazität der Datenquelle überschreitet. In diesem Fall kann die Komplexität der Datenquellenabfrage verringert werden, indem verbundene Objekte explizit geladen werden.

Zum expliziten Laden verbundener Objekte muss die Load-Methode für eine Navigationseigenschaft aufgerufen werden, die eine EntityCollection<TEntity> oder EntityReference<TEntity> zurückgibt. Explizites Laden erfordert bei jedem Aufruf von Load einen Roundtrip zur Datenbank.

Hinweis

Wenn Sie beim Durchlaufen einer Auflistung zurückgegebener Objekte Load aufrufen, z. B. wenn Sie die foreach-Anweisung (For Each in Visual Basic) verwenden, muss der datenquellenspezifische Anbieter mehrere aktive Resultsets für eine einzelne Verbindung unterstützen. Bei einer SQL Server-Datenbank muss in der Verbindungszeichenfolge des Anbieters der Wert MultipleActiveResultSets = true angegeben werden.

Sie können auch die LoadProperty-Methode verwenden, wenn die Entitäten nicht über die EntityCollection<TEntity>-Eigenschaft oder die EntityReference<TEntity>-Eigenschaft verfügen. Dies ist bei Verwendung von POCO-Entitäten nützlich.

Obwohl durch das explizite Laden von verwandten Objekten die Anzahl der Joins und die Menge von redundanten Daten reduziert wird, erfordert Load wiederholte Verbindungen zur Datenbank, was beim expliziten Laden einer großen Anzahl von Objekten kostenintensiv sein kann.

Speichern von Änderungen

Wenn Sie die SaveChanges-Methode für einen ObjectContext aufrufen, wird für jedes hinzugefügte, aktualisierte oder gelöschte Objekt im Kontext jeweils ein eigener Befehl zum Erstellen, Aktualisieren oder Löschen generiert. Diese Befehle werden für die Datenquelle in einer einzelnen Transaktion ausgeführt. Wie bei Abfragen ist die Leistung von Vorgängen für die Erstellung, Aktualisierung und Löschung von der Komplexität der Zuordnung im konzeptionellen Modell abhängig.

Verteilte Transaktionen

Operationen in einer expliziten Transaktion, die Ressourcen erfordern, die vom verteilten Transaktionskoordinator (DTC) verwaltet werden, sind viel teurer als eine ähnliche Operation, die DTC nicht erfordert. In den folgenden Situationen findet eine Höherstufung zum DTC statt:

  • Eine explizite Transaktion mit einem Vorgang für eine SQL Server 2000-Datenbank oder andere Datenquellen, die ständig explizite Transaktionen auf den DTC hochstufen.

  • Eine explizite Transaktion mit einer Operation für SQL Server 2005, wenn die Verbindung von Entity Framework verwaltet wird. Dies tritt auf, da SQL Server 2005 immer dann auf einen DTC hochstuft, wenn eine Verbindung geschlossen und innerhalb einer einzelnen Transaktion erneut geöffnet wird. Dies ist das Standardverhalten von Entity Framework. Beim SQL Server 2008 wird diese DTC-Höherstufung nicht durchgeführt. Öffnen und schließen Sie explizit die Verbindung innerhalb der Transaktion, um diese Höherstufung zu vermeiden, wenn Sie SQL Server 2005 verwenden. Weitere Informationen finden Sie unter Verwalten von Verbindungen und Transaktionen.

Eine explizite Transaktion wird verwendet, wenn eine oder mehrere Operationen in einer System.Transactions-Transaktion ausgeführt werden. Weitere Informationen finden Sie unter Verwalten von Verbindungen und Transaktionen.

Strategien zum Verbessern der Leistung

Sie können die Gesamtleistung von Abfragen in Entity Framework mit den folgenden Strategien verbessern.

Vorabgenerieren von Sichten

Führt eine Anwendung eine Abfrage zum ersten Mal aus, ist das Generieren von Sichten auf Grundlage eines Entitätsmodells kostenintensiv. Verwenden Sie das Hilfsprogramm EdmGen.exe, um Sichten als Visual Basic- oder C#-Codedatei vorzugenerieren, die dem Projekt während des Entwurfs hinzugefügt werden kann. Sie können auch mit dem Text Template Transformation Toolkit (Textvorlagentransformations-Toolkit) vorkompilierte Sichten generieren. Zur Wahrung der Konsistenz mit der aktuellen Version des angegebenen Entitätsmodells werden vorgenerierte Sichten zur Laufzeit überprüft. Weitere Informationen finden Sie unter Gewusst wie: Vorgenerieren von Ansichten zur Verbesserung der Abfrageleistung.

Beim Arbeiten mit sehr umfangreichen Modellen ist Folgendes zu berücksichtigen:

Das .NET-Metadatenformat beschränkt die Anzahl der Zeichen in Benutzerzeichenfolgen in einer Binärdatei auf 16.777.215 (0xFFFFFF) Zeichen. Wenn Sie Sichten für ein sehr umfangreiches Modell generieren und die Sichtdatei diese Größenbeschränkung erreicht, wird der Compilerfehler "Kein freier logischer Speicherplatz zum Erstellen weiterer Benutzerzeichenfolgen" ausgegeben. Diese Größenbeschränkung gilt für alle verwalteten Binärdateien. Weitere Informationen finden Sie im Blog, in dem veranschaulicht wird, wie sich der Fehler beim Arbeiten mit umfangreichen und komplexen Modellen vermeiden lässt.

Verwenden der NoTracking-Mergeoption für Abfragen

Das Nachverfolgen von zurückgegebenen Objekten im Objektkontext bringt Kosten mit sich. Zur Ermittlung von Änderungen an Objekten und zur Gewährleistung, dass mehrere Anforderungen für die gleiche logische Entität die gleiche Objektinstanz zurückgeben, ist das Anfügen der Objekte an die ObjectContext-Instanz erforderlich. Wenn Sie nicht vorhaben, Objekte zu aktualisieren oder zu löschen, und keine Identitätsverwaltung benötigen, sollten Sie beim Ausführen von Abfragen die Verwendung von NoTracking-Mergeoptionen in Betracht ziehen.

Zurückgeben der richtigen Datenmenge

In einigen Szenarien ist das Angeben eines Abfragepfads mithilfe der Include-Methode viel schneller, da weniger Roundtrips zur Datenbank erforderlich sind. Allerdings sind in anderen Szenarien zusätzliche Roundtrips zur Datenbank zum Laden von verknüpften Objekten ggf. schneller, da die einfacheren Abfragen mit weniger Joins weniger redundante Daten liefern. Deswegen empfiehlt es sich, verschiedene Möglichkeiten zum Abrufen verknüpfter Objekte zu testen und so die leistungsfähigste Methode zu ermitteln. Weitere Informationen finden Sie unter Laden von verknüpften Objekten.

Gliedern Sie die Abfrageergebnisse in überschaubare Gruppen, um zu vermeiden, dass zu viel Daten in einer einzelnen Abfrage zurückgegeben werden. Weitere Informationen finden Sie unter Vorgehensweise: Blättern durch Abfrageergebnisse.

Einschränken des Bereichs des ObjectContext

In den meisten Fällen sollten Sie eine ObjectContext-Instanz innerhalb einer using-Anweisung (Using…End Using in Visual Basic) erstellen. Dies kann die Leistung verbessern, da die dem Objektkontext zugeordneten Ressourcen automatisch freigegeben werden, wenn der Anweisungsblock vom Code beendet wird. Wenn Steuerelemente jedoch an vom Objektkontext verwaltete Objekte gebunden werden, sollte die ObjectContext-Instanz so lange beibehalten werden, bis die Bindung benötigt und manuell freigegeben wird. Weitere Informationen finden Sie unter Verwalten von Verbindungen und Transaktionen.

Manuelles Öffnen der Datenbankverbindung

Wenn die Anwendung eine Reihe von Objektabfragen ausführt oder häufig SaveChanges aufruft, um Vorgänge zum Erstellen, Aktualisieren und Löschen in der Datenquelle zu speichern, muss Entity Framework die Verbindung zur Datenquelle ständig öffnen und schließen. Erwägen Sie in diesem Fall, die Verbindung am Start dieser Operationen manuell zu öffnen und die Verbindung zu schließen bzw. freizugeben, wenn die Vorgänge abgeschlossen sind. Weitere Informationen finden Sie unter Verwalten von Verbindungen und Transaktionen.

Leistungsdaten

Einige Leistungsdaten für Entity Framework wurden auf dem ADO.NET-Teamblog in den folgenden Beiträgen veröffentlicht:

Siehe auch