Maximieren der Leistung mit Entity Framework 4.0 in einer ASP.NET 4-Webanwendung
von Tom Dykstra
Diese Tutorialreihe baut auf der Contoso University-Webanwendung auf, die vom Erste Schritte mit der Tutorialreihe "Entity Framework 4.0" erstellt wird. Wenn Sie die vorherigen Tutorials nicht abgeschlossen haben, können Sie als Ausgangspunkt für dieses Tutorial die Anwendung herunterladen , die Sie erstellt hätten. Sie können auch die Anwendung herunterladen , die von der vollständigen Tutorialreihe erstellt wird. Wenn Sie Fragen zu den Tutorials haben, können Sie diese im ASP.NET Entity Framework-Forum posten.
Im vorherigen Tutorial haben Sie erfahren, wie Sie Parallelitätskonflikte behandeln. Dieses Tutorial zeigt Optionen zum Verbessern der Leistung einer ASP.NET Webanwendung, die Entity Framework verwendet. Sie lernen verschiedene Methoden zur Leistungsmaximierung oder zur Diagnose von Leistungsproblemen kennen.
Informationen, die in den folgenden Abschnitten dargestellt werden, sind wahrscheinlich in einer Vielzahl von Szenarien nützlich:
- Effizientes Laden verwandter Daten.
- Verwalten des Ansichtszustands.
Informationen in den folgenden Abschnitten können hilfreich sein, wenn Sie über einzelne Abfragen verfügen, die Leistungsprobleme darstellen:
- Verwenden Sie die
NoTracking
Mergeoption. - LinQ-Abfragen vorkompilieren.
- Untersuchen Sie abfragebefehle, die an die Datenbank gesendet werden.
Die im folgenden Abschnitt dargestellten Informationen sind potenziell nützlich für Anwendungen, die über extrem große Datenmodelle verfügen:
- Ansichten vorab generieren.
Hinweis
Die Leistung von Webanwendungen wird von vielen Faktoren beeinflusst, darunter die Größe der Anforderungs- und Antwortdaten, die Geschwindigkeit von Datenbankabfragen, die Anzahl der Anforderungen, die der Server in die Warteschlange stellen kann und wie schnell er sie warten kann, und sogar die Effizienz aller Clientskriptbibliotheken, die Sie möglicherweise verwenden. Wenn die Leistung in Ihrer Anwendung entscheidend ist oder Tests oder Erfahrungen zeigen, dass die Anwendungsleistung nicht zufriedenstellend ist, sollten Sie das normale Protokoll für die Leistungsoptimierung befolgen. Messen Sie, wo Leistungsengpässe auftreten, und adressieren Sie dann die Bereiche, die die größte Auswirkung auf die Gesamtleistung der Anwendung haben.
Dieses Thema konzentriert sich hauptsächlich auf Möglichkeiten, wie Sie die Leistung von Entity Framework in ASP.NET potenziell verbessern können. Die hier aufgeführten Vorschläge sind nützlich, wenn Sie feststellen, dass der Datenzugriff einer der Leistungsengpässe in Ihrer Anwendung ist. Außer wie erwähnt, sollten die hier erläuterten Methoden nicht als "bewährte Methoden" im Allgemeinen betrachtet werden – viele von ihnen sind nur in Ausnahmefällen oder zur Behebung von sehr spezifischen Arten von Leistungsengpässen geeignet.
Um das Tutorial zu starten, starten Sie Visual Studio, und öffnen Sie die Contoso University-Webanwendung, mit der Sie im vorherigen Tutorial gearbeitet haben.
Effizientes Laden verwandter Daten
Es gibt mehrere Möglichkeiten, wie Entity Framework verwandte Daten in die Navigationseigenschaften einer Entität laden kann:
Lazy Loading (verzögertes Laden). Wenn die Entität zuerst gelesen wird, werden verwandte Daten nicht abgerufen. Wenn Sie jedoch zum ersten Mal versuchen, auf eine Navigationseigenschaft zuzugreifen, werden die für diese Navigationseigenschaft erforderlichen Daten automatisch abgerufen. Dies führt dazu, dass mehrere Abfragen an die Datenbank gesendet werden – eine für die Entität selbst und eine für jedes Mal, wenn verwandte Daten für die Entität abgerufen werden müssen.
Eager Loading (vorzeitiges Laden). Wenn die Entität gelesen wird, werden ihre verwandten Daten mit ihr abgerufen. Dies führt normalerweise zu einer einzelnen Joinabfrage, die alle Daten abruft, die erforderlich sind. Sie geben eager loading mithilfe der Include
-Methode an, wie Sie bereits in diesen Tutorials gesehen haben.
Explizites Laden. Dies ähnelt dem verzögerten Laden, mit der Ausnahme, dass Sie die zugehörigen Daten explizit im Code abrufen. Dies geschieht nicht automatisch, wenn Sie auf eine Navigationseigenschaft zugreifen. Sie laden verknüpfte Daten manuell mithilfe der
Load
-Methode der Navigationseigenschaft für Sammlungen, oder sie verwenden dieLoad
-Methode der Verweiseigenschaft für Eigenschaften, die ein einzelnes Objekt enthalten. (Sie rufen beispielsweise diePersonReference.Load
-Methode auf, um diePerson
Navigationseigenschaft einerDepartment
Entität zu laden.)
Da sie die Eigenschaftswerte nicht sofort abrufen, werden verzögertes Laden und explizites Laden auch als verzögertes Laden bezeichnet.
Verzögertes Laden ist das Standardverhalten für einen Objektkontext, der vom Designer generiert wurde. Wenn Sie SchoolModel.Designer öffnen. Cs-Datei, die die Objektkontextklasse definiert, finden Sie drei Konstruktormethoden, und jede von ihnen enthält die folgende Anweisung:
this.ContextOptions.LazyLoadingEnabled = true;
Wenn Sie wissen, dass Sie verwandte Daten für jede abgerufene Entität benötigen, bietet eager loading die beste Leistung, da eine einzelne Abfrage, die an die Datenbank gesendet wird, in der Regel effizienter ist als separate Abfragen für jede abgerufene Entität. Wenn Sie dagegen nur selten oder nur für eine kleine Gruppe der Entitäten auf die Navigationseigenschaften einer Entität zugreifen müssen, kann das verzögerte Laden oder explizites Laden effizienter sein, da beim ausführenden Laden mehr Daten abgerufen werden würden, als Sie benötigen.
In einer Webanwendung kann das verzögerte Laden sowieso relativ wenig wert sein, da Benutzeraktionen, die sich auf die Notwendigkeit verwandter Daten auswirken, im Browser stattfinden, der keine Verbindung mit dem Objektkontext hat, der die Seite gerendert hat. Wenn Sie jedoch ein Steuerelement mit Daten binden, wissen Sie in der Regel, welche Daten Sie benötigen, und daher ist es im Allgemeinen am besten, eifrig laden oder verzögert laden zu wählen, je nachdem, was in jedem Szenario geeignet ist.
Darüber hinaus kann ein datengebundenes Steuerelement ein Entitätsobjekt verwenden, nachdem der Objektkontext gelöscht wurde. In diesem Fall schlägt der Versuch, eine Navigationseigenschaft zu lazy laden, fehl. Die Fehlermeldung, die Sie erhalten, ist eindeutig: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
"
Das EntityDataSource
Steuerelement deaktiviert standardmäßig verzögertes Laden. Für das ObjectDataSource
Steuerelement, das Sie für das aktuelle Tutorial verwenden (oder wenn Sie über Seitencode auf den Objektkontext zugreifen), gibt es mehrere Möglichkeiten, das verzögerte Laden standardmäßig deaktiviert zu machen. Sie können sie deaktivieren, wenn Sie einen Objektkontext instanziieren. Beispielsweise können Sie der Konstruktormethode der SchoolRepository
-Klasse die folgende Zeile hinzufügen:
context.ContextOptions.LazyLoadingEnabled = false;
Für die Contoso University-Anwendung deaktivieren Sie den Objektkontext automatisch verzögertes Laden, sodass diese Eigenschaft nicht festgelegt werden muss, wenn ein Kontext instanziiert wird.
Öffnen Sie das Datenmodell SchoolModel.edmx , klicken Sie auf die Entwurfsoberfläche, und legen Sie dann im Eigenschaftenbereich die Eigenschaft Lazy Loading Enabled auf fest False
. Speichern und schließen Sie das Datenmodell.
Verwalten des Ansichtszustands
Um Aktualisierungsfunktionen bereitzustellen, muss eine ASP.NET Webseite die ursprünglichen Eigenschaftswerte einer Entität speichern, wenn eine Seite gerendert wird. Während der Postbackverarbeitung kann das Steuerelement den ursprünglichen Zustand der Entität neu erstellen und die Methode der Entität Attach
aufrufen, bevor Änderungen angewendet und die SaveChanges
-Methode aufgerufen wird. Standardmäßig verwenden ASP.NET Web Forms Datensteuerelemente den Ansichtszustand, um die ursprünglichen Werte zu speichern. Der Ansichtszustand kann sich jedoch auf die Leistung auswirken, da er in ausgeblendeten Feldern gespeichert wird, wodurch die Größe der Seite, die an den und vom Browser gesendet wird, erheblich erhöht werden kann.
Techniken zum Verwalten des Ansichtszustands oder Alternativen wie der Sitzungszustand sind für Entity Framework nicht eindeutig, sodass dieses Tutorial nicht im Detail auf dieses Thema eingegangen wird. Weitere Informationen finden Sie unter den Links am Ende des Tutorials.
Version 4 von ASP.NET bietet jedoch eine neue Möglichkeit, mit dem Ansichtszustand zu arbeiten, die jeder ASP.NET Entwickler von Web Forms Anwendungen kennen sollte: die ViewStateMode
-Eigenschaft. Diese neue Eigenschaft kann auf Seiten- oder Steuerelementebene festgelegt werden. Sie ermöglicht es Ihnen, den Ansichtszustand standardmäßig für eine Seite zu deaktivieren und ihn nur für Steuerelemente zu aktivieren, die ihn benötigen.
Für Anwendungen, in denen die Leistung entscheidend ist, empfiehlt es sich, den Ansichtszustand immer auf Seitenebene zu deaktivieren und nur für Steuerelemente zu aktivieren, die ihn erfordern. Die Größe des Ansichtszustands auf den Seiten der Contoso University würde durch diese Methode nicht wesentlich verringert, aber um zu sehen, wie er funktioniert, werden Sie dies für die Seite Instructors.aspx tun. Diese Seite enthält viele Steuerelemente, einschließlich eines Label
Steuerelements, für das der Ansichtszustand deaktiviert ist. Für keines der Steuerelemente auf dieser Seite muss der Ansichtszustand aktiviert sein. (Die DataKeyNames
-Eigenschaft des Steuerelements gibt den GridView
Zustand an, der zwischen Postbacks beibehalten werden muss, aber diese Werte werden im Steuerungszustand beibehalten, der von der ViewStateMode
-Eigenschaft nicht betroffen ist.)
Das Page
Direktiven- und Label
Steuerelementmarkup ähnelt derzeit dem folgenden Beispiel:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors" %>
...
<asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label>
...
Nehmen Sie die folgenden Änderungen vor:
- Fügen Sie der
Page
-Direktive hinzuViewStateMode="Disabled"
. - Entfernen Sie
ViewStateMode="Disabled"
aus demLabel
Steuerelement.
Das Markup ähnelt nun dem folgenden Beispiel:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="Instructors.aspx.cs" Inherits="ContosoUniversity.Instructors"
ViewStateMode="Disabled" %>
...
<asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false"></asp:Label>
...
Der Ansichtszustand ist jetzt für alle Steuerelemente deaktiviert. Wenn Sie später ein Steuerelement hinzufügen, das den Ansichtszustand verwenden muss, müssen Sie lediglich das ViewStateMode="Enabled"
-Attribut für dieses Steuerelement einschließen.
Verwenden der NoTracking-Mergeoption
Wenn ein Objektkontext Datenbankzeilen abruft und Entitätsobjekte erstellt, die diese darstellen, verfolgt er standardmäßig auch diese Entitätsobjekte mithilfe des Objektzustands-Managers nach. Diese Nachverfolgungsdaten fungieren als Cache und werden verwendet, wenn Sie eine Entität aktualisieren. Da eine Webanwendung in der Regel über kurzlebige Objektkontextinstanzen verfügt, geben Abfragen häufig Daten zurück, die nicht nachverfolgt werden müssen, da der Objektkontext, der sie liest, verworfen wird, bevor eine der gelesenen Entitäten erneut verwendet oder aktualisiert wird.
Im Entity Framework können Sie angeben, ob der Objektkontext Entitätsobjekte nachverfolgt, indem Sie eine Mergeoption festlegen. Sie können die Mergeoption für einzelne Abfragen oder für Entitätssätze festlegen. Wenn Sie sie für einen Entitätssatz festlegen, bedeutet dies, dass Sie die Standard-Mergeoption für alle Abfragen festlegen, die für diesen Entitätssatz erstellt werden.
Für die Contoso University-Anwendung ist die Nachverfolgung für keine der Entitätssätze erforderlich, auf die Sie aus dem Repository zugreifen. Daher können Sie die Mergeoption NoTracking
für diese Entitätssätze auf festlegen, wenn Sie den Objektkontext in der Repositoryklasse instanziieren. (Beachten Sie, dass in diesem Tutorial das Festlegen der Mergeoption keine spürbaren Auswirkungen auf die Leistung der Anwendung hat. Die NoTracking
Option führt wahrscheinlich nur in bestimmten Szenarien mit hohem Datenvolumen zu einer beobachtbaren Leistungsverbesserung.)
Öffnen Sie im Ordner DAL die Datei SchoolRepository.cs , und fügen Sie eine Konstruktormethode hinzu, die die Mergeoption für die Entitätssätze festlegt, auf die das Repository zugreift:
public SchoolRepository()
{
context.Departments.MergeOption = MergeOption.NoTracking;
context.InstructorNames.MergeOption = MergeOption.NoTracking;
context.OfficeAssignments.MergeOption = MergeOption.NoTracking;
}
Vorkompilieren von LINQ-Abfragen
Wenn Entity Framework eine Entity SQL-Abfrage zum ersten Mal innerhalb der Lebensdauer eines bestimmten ObjectContext
instance ausführt, dauert es einige Zeit, bis die Abfrage kompiliert wird. Das Ergebnis der Kompilierung wird zwischengespeichert, was bedeutet, dass nachfolgende Ausführungen der Abfrage viel schneller ausgeführt werden. LINQ-Abfragen folgen einem ähnlichen Muster, mit der Ausnahme, dass ein Teil der zum Kompilieren der Abfrage erforderlichen Arbeit bei jeder Ausführung der Abfrage erledigt wird. Anders ausgedrückt: Bei LINQ-Abfragen werden standardmäßig nicht alle Ergebnisse der Kompilierung zwischengespeichert.
Wenn Sie über eine LINQ-Abfrage verfügen, die in der Lebensdauer eines Objektkontexts wiederholt ausgeführt werden soll, können Sie Code schreiben, der bewirkt, dass alle Ergebnisse der Kompilierung bei der ersten Ausführung der LINQ-Abfrage zwischengespeichert werden.
Zur Veranschaulichung führen Sie dies für zwei Get
Methoden in der SchoolRepository
-Klasse aus, von denen eine keine Parameter (die -Methode) und eine , die GetInstructorNames
einen Parameter (die GetDepartmentsByAdministrator
-Methode) benötigt. Diese Methoden, wie sie jetzt stehen, müssen eigentlich nicht kompiliert werden, da sie keine LINQ-Abfragen sind:
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.InstructorNames.OrderBy("it.FullName").ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
return new ObjectQuery<Department>("SELECT VALUE d FROM Departments as d", context, MergeOption.NoTracking).Include("Person").Where(d => d.Administrator == administrator).ToList();
}
Damit Sie kompilierte Abfragen ausprobieren können, gehen Sie jedoch so fort, als ob diese als die folgenden LINQ-Abfragen geschrieben worden seien:
public IEnumerable<InstructorName> GetInstructorNames()
{
return (from i in context.InstructorNames orderby i.FullName select i).ToList();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
context.Departments.MergeOption = MergeOption.NoTracking;
return (from d in context.Departments where d.Administrator == administrator select d).ToList();
}
Sie können den Code in diesen Methoden in den oben gezeigten ändern und die Anwendung ausführen, um zu überprüfen, ob er funktioniert, bevor Sie fortfahren. Aber die folgenden Anweisungen springen direkt in die Erstellung vorkompilierter Versionen dieser Versionen.
Erstellen Sie eine Klassendatei im Ordner DAL , nennen Sie sie SchoolEntities.cs, und ersetzen Sie den vorhandenen Code durch den folgenden Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Objects;
namespace ContosoUniversity.DAL
{
public partial class SchoolEntities
{
private static readonly Func<SchoolEntities, IQueryable<InstructorName>> compiledInstructorNamesQuery =
CompiledQuery.Compile((SchoolEntities context) => from i in context.InstructorNames orderby i.FullName select i);
public IEnumerable<InstructorName> CompiledInstructorNamesQuery()
{
return compiledInstructorNamesQuery(this).ToList();
}
private static readonly Func<SchoolEntities, Int32, IQueryable<Department>> compiledDepartmentsByAdministratorQuery =
CompiledQuery.Compile((SchoolEntities context, Int32 administrator) => from d in context.Departments.Include("Person") where d.Administrator == administrator select d);
public IEnumerable<Department> CompiledDepartmentsByAdministratorQuery(Int32 administrator)
{
return compiledDepartmentsByAdministratorQuery(this, administrator).ToList();
}
}
}
Dieser Code erstellt eine partielle Klasse, die die automatisch generierte Objektkontextklasse erweitert. Die partielle Klasse enthält zwei kompilierte LINQ-Abfragen, die die Compile
-Methode der CompiledQuery
-Klasse verwenden. Außerdem werden Methoden erstellt, die Sie zum Aufrufen der Abfragen verwenden können. Speichern und schließen Sie diese Datei.
Ändern Sie als Nächstes in SchoolRepository.cs die vorhandenen GetInstructorNames
Methoden und GetDepartmentsByAdministrator
in der Repositoryklasse, sodass die kompilierten Abfragen aufgerufen werden:
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.CompiledInstructorNamesQuery();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
return context.CompiledDepartmentsByAdministratorQuery(administrator);
}
Führen Sie die Seite Departments.aspx aus , um zu überprüfen, ob sie wie zuvor funktioniert. Die GetInstructorNames
-Methode wird aufgerufen, um die Dropdownliste des Administrators aufzufüllen, und die GetDepartmentsByAdministrator
Methode wird aufgerufen, wenn Sie auf Aktualisieren klicken, um zu überprüfen, ob kein Dozent ein Administrator mehrerer Abteilungen ist.
Sie haben Abfragen in der Contoso University-Anwendung nur vorkompiliert, um zu sehen, wie dies funktioniert, nicht, weil dies die Leistung messbar verbessern würde. Das Vorkompilieren von LINQ-Abfragen führt zu einer zusätzlichen Komplexität ihres Codes. Stellen Sie daher sicher, dass Sie dies nur für Abfragen tun, die tatsächlich Leistungsengpässe in Ihrer Anwendung darstellen.
Untersuchen von Abfragen, die an die Datenbank gesendet werden
Wenn Sie Leistungsprobleme untersuchen, ist es manchmal hilfreich, die genauen SQL-Befehle zu kennen, die das Entity Framework an die Datenbank sendet. Wenn Sie mit einem IQueryable
-Objekt arbeiten, besteht eine Möglichkeit dazu darin, die ToTraceString
-Methode zu verwenden.
Ändern Sie in SchoolRepository.cs den Code in der GetDepartmentsByName
-Methode so, dass er dem folgenden Beispiel entspricht:
public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
...
var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Include("Person").Include("Courses").Where(d => d.Name.Contains(nameSearchString));
string commandText = ((ObjectQuery)departments).ToTraceString();
return departments.ToList();
}
Die departments
Variable muss nur in einen ObjectQuery
Typ umgewandelt werden, da die Where
-Methode am Ende der vorherigen Zeile ein IQueryable
-Objekt erstellt. Ohne die Where
-Methode wäre die Umwandlung nicht erforderlich.
Legen Sie einen Haltepunkt in der return
Zeile fest, und führen Sie dann die Seite Departments.aspx im Debugger aus. Wenn Sie den Haltepunkt erreichen, untersuchen Sie die commandText
Variable im Fenster Lokal , und verwenden Sie die Textschnellansicht (die Lupe in der Spalte Wert ), um den Wert im Fenster Textschnellansicht anzuzeigen. Sie können den gesamten SQL-Befehl sehen, der sich aus diesem Code ergibt:
Alternativ bietet das IntelliTrace-Feature in Visual Studio Ultimate eine Möglichkeit zum Anzeigen von SQL-Befehlen, die vom Entity Framework generiert wurden, ohne dass Sie Ihren Code ändern oder sogar einen Haltepunkt festlegen müssen.
Hinweis
Sie können die folgenden Verfahren nur ausführen, wenn Sie über Visual Studio Ultimate verfügen.
Stellen Sie den ursprünglichen Code in der GetDepartmentsByName
-Methode wieder her, und führen Sie dann die Seite Departments.aspx im Debugger aus.
Wählen Sie in Visual Studio das Menü Debuggen , dann IntelliTrace und dann IntelliTrace-Ereignisse aus.
Klicken Sie im Fenster IntelliTrace auf Alle unterbrechen.
Im IntelliTrace-Fenster wird eine Liste der zuletzt verwendeten Ereignisse angezeigt:
Klicken Sie auf die Zeile ADO.NET . Sie wird erweitert, um den Befehlstext anzuzeigen:
Sie können die gesamte Befehlstextzeichenfolge aus dem Fenster Lokal in die Zwischenablage kopieren.
Angenommen, Sie haben mit einer Datenbank mit mehr Tabellen, Beziehungen und Spalten als die einfache School
Datenbank gearbeitet. Möglicherweise stellen Sie fest, dass eine Abfrage, die alle benötigten Informationen in einer einzelnen Select
Anweisung sammelt, die mehrere Join
Klauseln enthält, zu komplex ist, um effizient zu arbeiten. In diesem Fall können Sie von "Eager Loading" zum expliziten Laden wechseln, um die Abfrage zu vereinfachen.
Versuchen Sie beispielsweise, den Code in der GetDepartmentsByName
-Methode in SchoolRepository.cs zu ändern. Derzeit verfügen Sie in dieser Methode über eine Objektabfrage, die Include
Methoden für die Navigationseigenschaften Person
und Courses
enthält. Ersetzen Sie die return
-Anweisung durch Code, der explizit geladen wird, wie im folgenden Beispiel gezeigt:
public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
...
var departments = new ObjectQuery<Department>("SELECT VALUE d FROM Departments AS d", context).OrderBy("it." + sortExpression).Where(d => d.Name.Contains(nameSearchString)).ToList();
foreach (Department d in departments)
{
d.Courses.Load();
d.PersonReference.Load();
}
return departments;
}
Führen Sie die Seite Departments.aspx im Debugger aus, und überprüfen Sie das IntelliTrace-Fenster wie zuvor erneut. Jetzt, wo es zuvor eine einzelne Abfrage gab, sehen Sie eine lange Sequenz von ihnen.
Klicken Sie auf die erste ADO.NET Zeile, um zu sehen, was mit der zuvor angezeigten komplexen Abfrage geschehen ist.
Die Abfrage von Abteilungen ist zu einer einfachen Select
Abfrage ohne Join
Klausel geworden, aber es folgen separate Abfragen, die verwandte Kurse und einen Administrator abrufen, der einen Satz von zwei Abfragen für jede Abteilung verwendet, die von der ursprünglichen Abfrage zurückgegeben wird.
Hinweis
Wenn Sie das verzögerte Laden aktiviert lassen, kann das hier angezeigte Muster, bei dem dieselbe Abfrage mehrmals wiederholt wird, durch verzögertes Laden entstehen. Ein Muster, das Sie in der Regel vermeiden möchten, ist das verzögerte Laden verwandter Daten für jede Zeile der primären Tabelle. Wenn Sie sich nicht vergewissert haben, dass eine einzelne Joinabfrage zu komplex ist, um effizient zu sein, könnten Sie in solchen Fällen die Leistung in der Regel verbessern, indem Sie die primäre Abfrage so ändern, dass "Eager Loading" verwendet wird.
Vorab generierende Sichten
Wenn ein ObjectContext
Objekt zum ersten Mal in einer neuen Anwendungsdomäne erstellt wird, generiert Entity Framework eine Reihe von Klassen, die für den Zugriff auf die Datenbank verwendet werden. Diese Klassen werden als Sichten bezeichnet, und wenn Sie über ein sehr umfangreiches Datenmodell verfügen, kann das Generieren dieser Ansichten die Antwort der Website auf die erste Anforderung für eine Seite verzögern, nachdem eine neue Anwendungsdomäne initialisiert wurde. Sie können diese Verzögerung bei der ersten Anforderung verringern, indem Sie die Ansichten zur Kompilierzeit und nicht zur Laufzeit erstellen.
Hinweis
Wenn Ihre Anwendung kein extrem umfangreiches Datenmodell aufweist oder wenn sie über ein großes Datenmodell verfügt, Sie sich aber keine Sorgen über ein Leistungsproblem machen, das sich nur auf die erste Seitenanforderung nach der Wiederverwendung von IIS auswirkt, können Sie diesen Abschnitt überspringen. Die Ansichtserstellung erfolgt nicht jedes Mal, wenn Sie ein ObjectContext
Objekt instanziieren, da die Ansichten in der Anwendungsdomäne zwischengespeichert werden. Wenn Sie Ihre Anwendung nicht häufig in IIS wiederverwenden, würden daher nur sehr wenige Seitenanforderungen von vorab generierten Sichten profitieren.
Sie können Ansichten vorab generieren, indem Sie das BefehlszeilentoolEdmGen.exe oder eine T4-Vorlage ( Text Template Transformation Toolkit ) verwenden. In diesem Tutorial verwenden Sie eine T4-Vorlage.
Fügen Sie im Ordner DAL eine Datei mithilfe der Vorlage Textvorlage hinzu (sie befindet sich unter dem Knoten Allgemein in der Liste Installierte Vorlagen ), und benennen Sie sie SchoolModel.Views.tt. Ersetzen Sie den vorhandenen Code in der Datei durch den folgenden Code:
<#
/***************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
***************************************************************************/
#>
<#
//
// TITLE: T4 template to generate views for an EDMX file in a C# project
//
// DESCRIPTION:
// This is a T4 template to generate views in C# for an EDMX file in C# projects.
// The generated views are automatically compiled into the project's output assembly.
//
// This template follows a simple file naming convention to determine the EDMX file to process:
// - It assumes that [edmx-file-name].Views.tt will process and generate views for [edmx-file-name].EDMX
// - The views are generated in the code behind file [edmx-file-name].Views.cs
//
// USAGE:
// Do the following to generate views for an EDMX file (e.g. Model1.edmx) in a C# project
// 1. In Solution Explorer, right-click the project node and choose "Add...Existing...Item" from the context menu
// 2. Browse to and choose this .tt file to include it in the project
// 3. Ensure this .tt file is in the same directory as the EDMX file to process
// 4. In Solution Explorer, rename this .tt file to the form [edmx-file-name].Views.tt (e.g. Model1.Views.tt)
// 5. In Solution Explorer, right-click Model1.Views.tt and choose "Run Custom Tool" to generate the views
// 6. The views are generated in the code behind file Model1.Views.cs
//
// TIPS:
// If you have multiple EDMX files in your project then make as many copies of this .tt file and rename appropriately
// to pair each with each EDMX file.
//
// To generate views for all EDMX files in the solution, click the "Transform All Templates" button in the Solution Explorer toolbar
// (its the rightmost button in the toolbar)
//
#>
<#
//
// T4 template code follows
//
#>
<#@ template language="C#" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<#
// Find EDMX file to process: Model1.Views.tt generates views for Model1.EDMX
string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";
string edmxFilePath = Path.Combine(Path.GetDirectoryName(this.Host.TemplateFile), edmxFileName);
if (File.Exists(edmxFilePath))
{
// Call helper class to generate pre-compiled views and write to output
this.WriteLine(GenerateViews(edmxFilePath));
}
else
{
this.Error(String.Format("No views were generated. Cannot find file {0}. Ensure the project has an EDMX file and the file name of the .tt file is of the form [edmx-file-name].Views.tt", edmxFilePath));
}
// All done!
#>
<#+
private String GenerateViews(string edmxFilePath)
{
MetadataLoader loader = new MetadataLoader(this);
MetadataWorkspace workspace;
if(!loader.TryLoadAllMetadata(edmxFilePath, out workspace))
{
this.Error("Error in the metadata");
return String.Empty;
}
String generatedViews = String.Empty;
try
{
using (StreamWriter writer = new StreamWriter(new MemoryStream()))
{
StorageMappingItemCollection mappingItems = (StorageMappingItemCollection)workspace.GetItemCollection(DataSpace.CSSpace);
// Initialize the view generator to generate views in C#
EntityViewGenerator viewGenerator = new EntityViewGenerator();
viewGenerator.LanguageOption = LanguageOption.GenerateCSharpCode;
IList<EdmSchemaError> errors = viewGenerator.GenerateViews(mappingItems, writer);
foreach (EdmSchemaError e in errors)
{
// log error
this.Error(e.Message);
}
MemoryStream memStream = writer.BaseStream as MemoryStream;
generatedViews = Encoding.UTF8.GetString(memStream.ToArray());
}
}
catch (Exception ex)
{
// log error
this.Error(ex.ToString());
}
return generatedViews;
}
#>
Dieser Code generiert Ansichten für eine EDMX-Datei , die sich im selben Ordner wie die Vorlage befindet und denselben Namen wie die Vorlagendatei hat. Wenn Ihre Vorlagendatei beispielsweise SchoolModel.Views.tt heißt, sucht sie nach einer Datenmodelldatei namens SchoolModel.edmx.
Speichern Sie die Datei, klicken Sie dann mit der rechten Maustaste auf die Datei in Projektmappen-Explorer, und wählen Sie Benutzerdefiniertes Tool ausführen aus.
Visual Studio generiert eine Codedatei, die die Ansichten erstellt, die auf der Vorlage den Namen SchoolModel.Views.cs trägt. (Möglicherweise haben Sie bemerkt, dass die Codedatei generiert wird, bevor Sie Benutzerdefiniertes Tool ausführen auswählen, sobald Sie die Vorlagendatei speichern.)
Sie können die Anwendung jetzt ausführen und überprüfen, ob sie wie zuvor funktioniert.
Weitere Informationen zu vorab generierten Sichten finden Sie in den folgenden Ressourcen:
- Vorgehensweise: Vorabgenerierung von Sichten zur Verbesserung der Abfrageleistung auf der MSDN-Website . Erläutert, wie Sie das
EdmGen.exe
Befehlszeilentool verwenden, um Ansichten vorab zu generieren. - Isolieren der Leistung mit vorkompilierten/vorgenerierten Sichten im Entity Framework 4 im Blog des Windows Server AppFabric-Kundenberatungsteams.
Dies schließt die Einführung in die Verbesserung der Leistung in einer ASP.NET Webanwendung ab, die Entity Framework verwendet. Weitere Informationen finden Sie in den folgenden Ressourcen:
- Überlegungen zur Leistung (Entity Framework) auf der MSDN-Website.
- Leistungsbezogene Beiträge im Entity Framework-Teamblog.
- EF-Mergeoptionen und kompilierte Abfragen. Blogbeitrag, in dem unerwartetes Verhalten von kompilierten Abfragen und Mergeoptionen wie erläutert wird
NoTracking
. Wenn Sie beabsichtigen, kompilierte Abfragen zu verwenden oder Einstellungen für Mergeoptionen in Ihrer Anwendung zu bearbeiten, lesen Sie dies zuerst. - Entity Framework-bezogene Beiträge im Data and Modeling Customer Advisory Team-Blog. Enthält Beiträge zu kompilierten Abfragen und Verwenden von Visual Studio 2010 Profiler zum Ermitteln von Leistungsproblemen.
- ASP.NET Empfehlungen für die Zustandsverwaltung.
- Verwenden von Entity Framework und ObjectDataSource: Benutzerdefiniertes Paging. Blogbeitrag, der auf der in diesen Tutorials erstellten ContosoUniversity-Anwendung aufbaut, um zu erläutern, wie Paging auf der Seite Departments.aspx implementiert wird.
Im nächsten Tutorial werden einige der wichtigen Verbesserungen an Entity Framework erläutert, die in Version 4 neu sind.