Share via


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.

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.

    Immagine05

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.

Immagine07

  • 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 il Load metodo della proprietà di riferimento per le proprietà che contengono un singolo oggetto. Ad esempio, si chiama il PersonReference.Load metodo per caricare la Person proprietà di spostamento di un'entità Department .

    Image06

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.

Immagine04

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" alla Page direttiva.
  • Rimuovere ViewStateMode="Disabled" dal Label 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.

Immagine03

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:

Immagine08

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.

Immagine11

Nella finestra IntelliTrace fare clic su Interrompi tutto.

Immagine12

Nella finestra IntelliTrace viene visualizzato un elenco di eventi recenti:

Immagine09

Fare clic sulla riga ADO.NET . Si espande per visualizzare il testo del comando:

Image10

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

Immagine13

Fare clic sulla prima riga ADO.NET per vedere cosa è successo alla query complessa visualizzata in precedenza.

Immagine14

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.

Immagine02

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.

Immagine01

È ora possibile eseguire l'applicazione e verificare che funzioni come in precedenza.

Per altre informazioni sulle viste pre-generate, vedere le risorse seguenti:

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:

L'esercitazione successiva esamina alcuni dei miglioramenti importanti apportati a Entity Framework che sono nuovi nella versione 4.