Share via


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.

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.

    Bild05

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.

Bild07

  • 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 die Load -Methode der Verweiseigenschaft für Eigenschaften, die ein einzelnes Objekt enthalten. (Sie rufen beispielsweise die PersonReference.Load -Methode auf, um die Person Navigationseigenschaft einer Department Entität zu laden.)

    Abbildung06

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.

Bild04

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 dem Label 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.

Bild03

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:

Image08

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.

Bild11

Klicken Sie im Fenster IntelliTrace auf Alle unterbrechen.

Bild12

Im IntelliTrace-Fenster wird eine Liste der zuletzt verwendeten Ereignisse angezeigt:

Bild09

Klicken Sie auf die Zeile ADO.NET . Sie wird erweitert, um den Befehlstext anzuzeigen:

Bild 10

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.

Bild13

Klicken Sie auf die erste ADO.NET Zeile, um zu sehen, was mit der zuvor angezeigten komplexen Abfrage geschehen ist.

Bild14

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.

Bild02

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.)

Bild01

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:

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:

Im nächsten Tutorial werden einige der wichtigen Verbesserungen an Entity Framework erläutert, die in Version 4 neu sind.