Ottimizzare le prestazioni con Entity Framework 4.0 in un'applicazione Web ASP.NET 4
di Tom Dykstra
Questa serie di esercitazioni si basa sull'applicazione Web Contoso University creata dalla Introduzione con la serie di esercitazioni di Entity Framework 4.0. Se non sono state completate le esercitazioni precedenti, come punto di partenza per questa esercitazione è possibile scaricare l'applicazione creata. È anche possibile scaricare l'applicazione creata dalla serie completa di esercitazioni. Se si hanno domande sulle esercitazioni, è possibile pubblicarli nel forum di Entity Framework ASP.NET.
Nell'esercitazione precedente è stato illustrato come gestire i conflitti di concorrenza. Questa esercitazione illustra le opzioni per migliorare le prestazioni di un'applicazione Web di ASP.NET che usa Entity Framework. Si apprenderà diversi metodi per ottimizzare le prestazioni o per diagnosticare i problemi di prestazioni.
Le informazioni presentate nelle sezioni seguenti sono probabilmente utili in un'ampia gamma di scenari:
- Caricare in modo efficiente i dati correlati.
- Gestire lo stato di visualizzazione.
Le informazioni presentate nelle sezioni seguenti potrebbero risultare utili se si dispone di singole query che presentano problemi di prestazioni:
- Usare l'opzione
NoTracking
merge. - Query LINQ precompilato.
- Esaminare i comandi di query inviati al database.
Le informazioni presentate nella sezione seguente sono potenzialmente utili per le applicazioni con modelli di dati estremamente grandi:
- Visualizzazioni pre-generate.
Nota
Le prestazioni dell'applicazione Web sono influenzate da molti fattori, tra cui le dimensioni dei dati di richiesta e risposta, la velocità delle query di database, il numero di richieste che il server può accodare e la velocità con cui può essere eseguito il servizio e anche l'efficienza di tutte le librerie di script client che si potrebbero usare. Se le prestazioni sono critiche nell'applicazione o se il test o l'esperienza mostrano che le prestazioni dell'applicazione non sono soddisfacenti, è consigliabile seguire il normale protocollo per l'ottimizzazione delle prestazioni. Misurare per determinare dove si verificano colli di bottiglia delle prestazioni e quindi affrontare le aree che avranno il maggior impatto sulle prestazioni complessive dell'applicazione.
Questo argomento è incentrato principalmente sui modi in cui è possibile migliorare le prestazioni in particolare di Entity Framework in ASP.NET. I suggerimenti qui sono utili se si determina che l'accesso ai dati è uno dei colli di bottiglia delle prestazioni nell'applicazione. Ad eccezione di quanto indicato, i metodi descritti qui non devono essere considerati "procedure consigliate" in generale , molti di essi sono appropriati solo in situazioni eccezionali o per affrontare tipi molto specifici di colli di bottiglia delle prestazioni.
Per iniziare l'esercitazione, avviare Visual Studio e aprire l'applicazione Web Contoso University usata nell'esercitazione precedente.
Caricamento efficiente dei dati correlati
Esistono diversi modi in cui Entity Framework può caricare i dati correlati nelle proprietà di spostamento di un'entità:
Caricamento lazy. Quando un'entità viene letta per la prima volta, i dati correlati non vengono recuperati. La prima volta che si tenta di accedere a una proprietà di navigazione, tuttavia, i dati necessari per quest'ultima vengono recuperati automaticamente. Ciò comporta più query inviate al database, una per l'entità stessa e una ogni volta che i dati correlati per l'entità devono essere recuperati.
Caricamento ansioso. Quando l'entità viene letta, i dati correlati corrispondenti vengono recuperati insieme ad essa. Ciò in genere ha come risultato una query join singola che recupera tutti i dati necessari. Specificare il caricamento ansioso usando il Include
metodo, come già illustrato in queste esercitazioni.
Caricamento esplicito. Questo è simile al caricamento lazy, ad eccezione del fatto che i dati correlati vengano recuperati in modo esplicito nel codice; non avviene automaticamente quando si accede a una proprietà di spostamento. I dati correlati vengono caricati manualmente usando il
Load
metodo della proprietà di spostamento per le raccolte oppure si usa ilLoad
metodo della proprietà di riferimento per le proprietà che contengono un singolo oggetto. Ad esempio, si chiama ilPersonReference.Load
metodo per caricare laPerson
proprietà di spostamento di un'entitàDepartment
.
Poiché non recuperano immediatamente i valori delle proprietà, il caricamento lazy e il caricamento esplicito sono entrambi noti come caricamento posticipato.
Il caricamento lazy è il comportamento predefinito per un contesto di oggetto generato dalla finestra di progettazione. Se si apre SchoolModel.Designer. file cs che definisce la classe di contesto dell'oggetto, sono disponibili tre metodi del costruttore e ognuno di essi include l'istruzione seguente:
this.ContextOptions.LazyLoadingEnabled = true;
In generale, se si sa che sono necessari dati correlati per ogni entità recuperata, il caricamento ansioso offre le prestazioni migliori, perché una singola query inviata al database è in genere più efficiente rispetto alle query separate per ogni entità recuperata. D'altra parte, se è necessario accedere alle proprietà di spostamento di un'entità solo raramente o solo per un piccolo set di entità, il caricamento lazy o il caricamento esplicito possono essere più efficienti, perché il caricamento ansioso recupera più dati che è necessario.
In un'applicazione Web, il caricamento lazy può essere di un valore relativamente minimo, perché le azioni utente che influiscono sulla necessità di dati correlati vengono eseguite nel browser, che non ha alcuna connessione al contesto dell'oggetto che ha eseguito il rendering della pagina. D'altra parte, quando si esegue il databinding di un controllo, si conoscono in genere i dati necessari e quindi è in genere consigliabile scegliere il caricamento o il caricamento posticipato in base a ciò che è appropriato in ogni scenario.
Inoltre, un controllo in ingresso dati può usare un oggetto entity dopo l'eliminazione del contesto dell'oggetto. In questo caso, un tentativo di caricamento ritardato di una proprietà di spostamento avrà esito negativo. Il messaggio di errore visualizzato è chiaro: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
"
Il EntityDataSource
controllo disabilita il caricamento lazy per impostazione predefinita. Per il ObjectDataSource
controllo usato per l'esercitazione corrente (o se si accede al contesto dell'oggetto dal codice della pagina), esistono diversi modi per rendere disabilitato il caricamento lazy per impostazione predefinita. È possibile disabilitarla quando si crea un'istanza di un contesto dell'oggetto. Ad esempio, è possibile aggiungere la riga seguente al metodo del costruttore della SchoolRepository
classe:
context.ContextOptions.LazyLoadingEnabled = false;
Per l'applicazione Contoso University, il contesto dell'oggetto disabilita automaticamente il caricamento lazy in modo che questa proprietà non sia impostata ogni volta che viene creata un'istanza di un contesto.
Aprire il modello di dati SchoolModel.edmx , fare clic sull'area di progettazione e quindi nel riquadro delle proprietà impostare la proprietà Lazy Load Enabled su False
. Salvare e chiudere il modello di dati.
Gestione dello stato di visualizzazione
Per fornire funzionalità di aggiornamento, una pagina Web ASP.NET deve archiviare i valori delle proprietà originali di un'entità quando viene eseguito il rendering di una pagina. Durante l'elaborazione postback il controllo può ricreare lo stato originale dell'entità e chiamare il metodo dell'entità Attach
prima di applicare modifiche e chiamare il SaveChanges
metodo. Per impostazione predefinita, Web Forms ASP.NET i controlli dati usano lo stato di visualizzazione per archiviare i valori originali. Tuttavia, lo stato di visualizzazione può influire sulle prestazioni, perché viene archiviato in campi nascosti che possono aumentare notevolmente le dimensioni della pagina inviata a e dal browser.
Le tecniche per la gestione dello stato di visualizzazione o alternative, ad esempio lo stato della sessione, non sono univoche per Entity Framework, quindi questa esercitazione non viene illustrata in dettaglio in questo argomento. Per altre informazioni, vedere i collegamenti alla fine dell'esercitazione.
Tuttavia, la versione 4 di ASP.NET offre un nuovo modo di usare lo stato di visualizzazione che ogni ASP.NET sviluppatore di applicazioni Web Forms deve essere a conoscenza della proprietà.ViewStateMode
Questa nuova proprietà può essere impostata a livello di pagina o controllo e consente di disabilitare lo stato di visualizzazione per impostazione predefinita per una pagina e abilitarlo solo per i controlli necessari.
Per le applicazioni in cui le prestazioni sono critiche, è consigliabile disabilitare sempre lo stato di visualizzazione a livello di pagina e abilitarlo solo per i controlli che lo richiedono. Le dimensioni dello stato di visualizzazione nelle pagine di Contoso University non saranno notevolmente ridotte da questo metodo, ma per vedere come funziona, lo si eseguirà per la pagina Instructors.aspx . La pagina contiene molti controlli, tra cui un Label
controllo con stato di visualizzazione disabilitato. Nessuno dei controlli in questa pagina deve effettivamente avere lo stato di visualizzazione abilitato. La DataKeyNames
proprietà del GridView
controllo specifica lo stato che deve essere mantenuto tra postback, ma questi valori vengono mantenuti nello stato di controllo, che non è interessato dalla ViewStateMode
proprietà.
La Page
direttiva e Label
il markup del controllo sono attualmente simili all'esempio seguente:
<%@ 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>
...
Apportare le modifiche seguenti:
- Aggiungere
ViewStateMode="Disabled"
allaPage
direttiva. - Rimuovere
ViewStateMode="Disabled"
dalLabel
controllo.
Il markup ora è simile all'esempio seguente:
<%@ 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>
...
Lo stato di visualizzazione è ora disabilitato per tutti i controlli. Se successivamente si aggiunge un controllo che deve usare lo stato di visualizzazione, è necessario includere l'attributo ViewStateMode="Enabled"
per tale controllo.
Uso dell'opzione di merge NoTracking
Quando un contesto oggetto recupera righe di database e crea oggetti di entità che li rappresentano, per impostazione predefinita tiene traccia di tali oggetti di entità usando la gestione dello stato dell'oggetto. Questi dati di rilevamento fungono da cache e vengono usati quando si aggiorna un'entità. Poiché un'applicazione Web include in genere istanze di contesto di oggetti di breve durata, le query spesso restituiscono dati che non devono essere rilevate, perché il contesto dell'oggetto che le legge verrà eliminato prima che una delle entità che legge vengano usate di nuovo o aggiornate.
In Entity Framework è possibile specificare se il contesto dell'oggetto tiene traccia degli oggetti entità impostando un'opzione di merge. È possibile impostare l'opzione di merge per singole query o per i set di entità. Se lo si imposta per un set di entità, ciò significa che si imposta l'opzione di unione predefinita per tutte le query create per tale set di entità.
Per l'applicazione Contoso University, il rilevamento non è necessario per uno dei set di entità a cui si accede dal repository, quindi è possibile impostare l'opzione merge su NoTracking
per tali set di entità quando si crea un'istanza del contesto dell'oggetto nella classe repository. Si noti che in questa esercitazione l'impostazione dell'opzione di merge non avrà un effetto evidente sulle prestazioni dell'applicazione. È probabile che l'opzione NoTracking
faccia un miglioramento delle prestazioni osservabile solo in determinati scenari con volumi di dati elevati.
Nella cartella DAL aprire il file SchoolRepository.cs e aggiungere un metodo di costruttore che imposta l'opzione di merge per i set di entità a cui accede il repository:
public SchoolRepository()
{
context.Departments.MergeOption = MergeOption.NoTracking;
context.InstructorNames.MergeOption = MergeOption.NoTracking;
context.OfficeAssignments.MergeOption = MergeOption.NoTracking;
}
Pre-compilazione di query LINQ
La prima volta che Entity Framework esegue una query Entity SQL all'interno della durata di un'istanza specificata ObjectContext
, è necessario tempo per compilare la query. Il risultato della compilazione viene memorizzato nella cache, il che significa che le esecuzioni successive della query sono molto più rapide. Le query LINQ seguono un modello simile, ad eccezione del fatto che alcune delle operazioni necessarie per compilare la query vengono eseguite ogni volta che viene eseguita la query. In altre parole, per le query LINQ, per impostazione predefinita non tutti i risultati della compilazione vengono memorizzati nella cache.
Se si dispone di una query LINQ che si prevede di eseguire ripetutamente nella vita di un contesto di oggetto, è possibile scrivere codice che causa la memorizzazione nella cache di tutti i risultati della compilazione la prima volta che viene eseguita la query LINQ.
Come illustrazione, questa operazione verrà eseguita per due Get
metodi nella SchoolRepository
classe , uno dei quali non accetta parametri (il GetInstructorNames
metodo) e uno che richiede un parametro (il GetDepartmentsByAdministrator
metodo ). Questi metodi attualmente non devono essere compilati perché non sono query LINQ:
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();
}
Tuttavia, in modo da poter provare le query compilate, procedere come se fossero state scritte come le query LINQ seguenti:
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();
}
È possibile modificare il codice in questi metodi in base a quanto illustrato in precedenza ed eseguire l'applicazione per verificare che funzioni prima di continuare. Tuttavia, le istruzioni seguenti consentono di creare versioni precompilata di esse.
Creare un file di classe nella cartella DAL , denominarlo SchoolEntities.cs e sostituire il codice esistente con il codice seguente:
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();
}
}
}
Questo codice crea una classe parziale che estende la classe di contesto oggetto generata automaticamente. La classe parziale include due query LINQ compilate usando il Compile
metodo della CompiledQuery
classe . Crea anche metodi che è possibile usare per chiamare le query. Salvare e chiudere il file.
Successivamente, in SchoolRepository.cs modificare i metodi e GetDepartmentsByAdministrator
esistenti GetInstructorNames
nella classe del repository in modo che chiamino le query compilate:
public IEnumerable<InstructorName> GetInstructorNames()
{
return context.CompiledInstructorNamesQuery();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
return context.CompiledDepartmentsByAdministratorQuery(administrator);
}
Eseguire la pagina Departments.aspx per verificare che funzioni come in precedenza. Il GetInstructorNames
metodo viene chiamato per popolare l'elenco a discesa amministratore e il GetDepartmentsByAdministrator
metodo viene chiamato quando si fa clic su Aggiorna per verificare che nessun insegnante sia un amministratore di più di un reparto.
Sono state compilate in modo preliminare le query nell'applicazione Contoso University solo per vedere come eseguire questa operazione, non perché potrebbe migliorare in modo misurabile le prestazioni. La precompilatazione delle query LINQ aggiunge un livello di complessità al codice, quindi assicurarsi di farlo solo per le query che rappresentano effettivamente colli di bottiglia delle prestazioni nell'applicazione.
Analisi delle query inviate al database
Quando si analizzano i problemi di prestazioni, talvolta è utile conoscere i comandi SQL esatti inviati da Entity Framework al database. Se si usa un IQueryable
oggetto, un modo per eseguire questa operazione consiste nell'usare il ToTraceString
metodo .
In SchoolRepository.cs modificare il codice nel metodo in GetDepartmentsByName
modo che corrisponda all'esempio seguente:
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();
}
È necessario eseguire il cast della departments
variabile a un ObjectQuery
tipo solo perché il Where
metodo alla fine della riga precedente crea un IQueryable
oggetto; senza il Where
metodo , il cast non sarebbe necessario.
Impostare un punto di interruzione nella return
riga e quindi eseguire la pagina Departments.aspx nel debugger. Quando si raggiunge il punto di interruzione, esaminare la commandText
variabile nella finestra Variabili locali e usare il visualizzatore di testo (la lente di ingrandimento nella colonna Valore ) per visualizzarne il valore nella finestra Visualizzatore di testo . È possibile visualizzare l'intero comando SQL risultante da questo codice:
In alternativa, la funzionalità IntelliTrace in Visual Studio Ultimate consente di visualizzare i comandi SQL generati da Entity Framework che non richiedono di modificare il codice o di impostare un punto di interruzione.
Nota
È possibile eseguire le procedure seguenti solo se si dispone di Visual Studio Ultimate.
Ripristinare il codice originale nel GetDepartmentsByName
metodo e quindi eseguire la pagina Departments.aspx nel debugger.
In Visual Studio selezionare il menu Debug , quindi IntelliTrace e quindi Eventi IntelliTrace.
Nella finestra IntelliTrace fare clic su Interrompi tutto.
Nella finestra IntelliTrace viene visualizzato un elenco di eventi recenti:
Fare clic sulla riga ADO.NET . Si espande per visualizzare il testo del comando:
È possibile copiare l'intera stringa di testo del comando negli Appunti dalla finestra Variabili locali .
Si supponga di usare un database con più tabelle, relazioni e colonne rispetto al database semplice School
. Si potrebbe notare che una query che raccoglie tutte le informazioni necessarie in una singola Select
istruzione contenente più Join
clausole diventa troppo complessa per funzionare in modo efficiente. In questo caso è possibile passare dal caricamento eager al caricamento esplicito per semplificare la query.
Ad esempio, provare a modificare il GetDepartmentsByName
codice nel metodo in SchoolRepository.cs. Attualmente in tale metodo è disponibile una query oggetto che include Include
metodi per le proprietà di Person
navigazione e Courses
. Sostituire l'istruzione con il return
codice che esegue il caricamento esplicito, come illustrato nell'esempio seguente:
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;
}
Eseguire la pagina Departments.aspx nel debugger e controllare di nuovo la finestra IntelliTrace come in precedenza. Ora, dove c'era una singola query prima, viene visualizzata una lunga sequenza di tali query.
Fare clic sulla prima riga ADO.NET per vedere cosa è successo alla query complessa visualizzata in precedenza.
La query dei reparti è diventata una query semplice Select
senza Join
clausola, ma è seguita da query separate che recuperano corsi correlati e un amministratore, usando un set di due query per ogni reparto restituito dalla query originale.
Nota
Se si lascia il caricamento differita abilitato, il modello visualizzato qui, con la stessa query ripetuta più volte, potrebbe risultare dal caricamento differita. Un criterio che in genere si vuole evitare è il caricamento differita dei dati correlati per ogni riga della tabella primaria. A meno che non sia stata verificata l'efficienza di una singola query di join, in genere sarebbe possibile migliorare le prestazioni in questi casi modificando la query primaria in modo da usare il caricamento eager.
Pre-generazione di viste
Quando un ObjectContext
oggetto viene creato per la prima volta in un nuovo dominio applicazione, Entity Framework genera un set di classi usate per accedere al database. Queste classi sono denominate viste e, se si dispone di un modello di dati di dimensioni molto grandi, la generazione di queste visualizzazioni può ritardare la risposta del sito Web alla prima richiesta di una pagina dopo l'inizializzazione di un nuovo dominio applicazione. È possibile ridurre questo ritardo della prima richiesta creando le viste in fase di compilazione anziché in fase di esecuzione.
Nota
Se l'applicazione non ha un modello di dati estremamente grande o se ha un modello di dati di grandi dimensioni, ma non si è interessati a un problema di prestazioni che interessa solo la prima richiesta di pagina dopo il riciclo di IIS, è possibile ignorare questa sezione. La creazione della visualizzazione non viene eseguita ogni volta che si crea un'istanza di un ObjectContext
oggetto, perché le visualizzazioni vengono memorizzate nella cache nel dominio dell'applicazione. Pertanto, a meno che non si stia riciclando di frequente l'applicazione in IIS, poche richieste di pagina trarranno vantaggio dalle visualizzazioni pregenerate.
È possibile pre-generare visualizzazioni usando lo strumento da riga di comando EdmGen.exe o usando un modello Text Template Transformation Toolkit (T4). In questa esercitazione si userà un modello T4.
Nella cartella DAL aggiungere un file usando il modello Modello di testo (si trova nel nodo Generale nell'elenco Modelli installati ) e denominarlo SchoolModel.Views.tt. Sostituire il codice esistente nel file con il codice seguente:
<#
/***************************************************************************
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;
}
#>
Questo codice genera visualizzazioni per un file con estensione edmx che si trova nella stessa cartella del modello e con lo stesso nome del file modello. Ad esempio, se il file modello è denominato SchoolModel.Views.tt, cercherà un file del modello di dati denominato SchoolModel.edmx.
Salvare il file, quindi fare clic con il pulsante destro del mouse sul file in Esplora soluzioni e scegliere Esegui strumento personalizzato.
Visual Studio genera un file di codice che crea le visualizzazioni denominate SchoolModel.Views.cs in base al modello. Si potrebbe notare che il file di codice viene generato anche prima di selezionare Esegui strumento personalizzato, non appena si salva il file modello.
È ora possibile eseguire l'applicazione e verificare che funzioni come in precedenza.
Per altre informazioni sulle viste pre-generate, vedere le risorse seguenti:
- Procedura: Pre-generare visualizzazioni per migliorare le prestazioni delle query nel sito Web MSDN. Viene illustrato come usare lo
EdmGen.exe
strumento da riga di comando per pre-generare visualizzazioni. - Isolamento delle prestazioni con viste precompilate/pre-generate in Entity Framework 4 nel blog del team di consulenza clienti di Windows Server AppFabric.
Questa operazione completa l'introduzione al miglioramento delle prestazioni in un'applicazione Web ASP.NET che usa Entity Framework. Per altre informazioni, vedere le risorse seguenti:
- Considerazioni sulle prestazioni (Entity Framework) nel sito Web MSDN.
- Post correlati alle prestazioni nel blog di Entity Framework Team.
- Opzioni di merge ef e query compilate. Post di blog che illustra i comportamenti imprevisti delle query compilate e delle opzioni di merge, ad
NoTracking
esempio . Se si prevede di usare query compilate o modificare le impostazioni delle opzioni di merge nell'applicazione, leggere prima di tutto. - Post correlati a Entity Framework nel blog del team di consulenza clienti di Data and Modeling. Include post sulle query compilate e l'uso di Visual Studio 2010 Profiler per individuare i problemi di prestazioni.
- ASP.NET Raccomandazioni sulla gestione dello stato.
- Uso di Entity Framework e ObjectDataSource: paging personalizzato. Post di blog basato sull'applicazione ContosoUniversity creata in queste esercitazioni per spiegare come implementare il paging nella pagina Departments.aspx .
L'esercitazione successiva esamina alcuni dei miglioramenti importanti apportati a Entity Framework che sono nuovi nella versione 4.