Partager via


Optimisation des performances avec Entity Framework 4.0 dans une application web ASP.NET 4

par Tom Dykstra

Cette série de tutoriels s’appuie sur l’application web Contoso University créée par le Prise en main avec la série de didacticiels Entity Framework 4.0. Si vous n’avez pas terminé les didacticiels précédents, comme point de départ de ce didacticiel, vous pouvez télécharger l’application que vous auriez créée. Vous pouvez également télécharger l’application créée par la série complète de tutoriels. Si vous avez des questions sur les didacticiels, vous pouvez les publier sur le forum ASP.NET Entity Framework.

Dans le tutoriel précédent, vous avez vu comment gérer les conflits d’accès concurrentiel. Ce tutoriel présente les options permettant d’améliorer les performances d’une application web ASP.NET qui utilise Entity Framework. Vous allez découvrir plusieurs méthodes permettant d’optimiser les performances ou de diagnostiquer les problèmes de performances.

Les informations présentées dans les sections suivantes sont susceptibles d’être utiles dans un large éventail de scénarios :

  • Chargez efficacement les données associées.
  • Gérer l’état d’affichage.

Les informations présentées dans les sections suivantes peuvent être utiles si vous avez des requêtes individuelles qui présentent des problèmes de performances :

  • Utilisez l’option NoTracking de fusion.
  • Précompiler les requêtes LINQ.
  • Examinez les commandes de requête envoyées à la base de données.

Les informations présentées dans la section suivante sont potentiellement utiles pour les applications qui ont des modèles de données extrêmement volumineux :

  • Prégénément des vues.

Remarque

Les performances des applications web sont affectées par de nombreux facteurs, notamment la taille des données de requête et de réponse, la vitesse des requêtes de base de données, le nombre de demandes que le serveur peut mettre en file d’attente et la rapidité avec laquelle il peut les traiter, et même l’efficacité des bibliothèques de script client que vous utilisez. Si les performances sont critiques dans votre application, ou si les tests ou l’expérience montrent que les performances de l’application ne sont pas satisfaisantes, vous devez suivre le protocole normal pour le réglage des performances. Mesure pour déterminer où se produisent les goulots d’étranglement des performances, puis traiter les domaines qui auront le plus d’impact sur les performances globales de l’application.

Cette rubrique se concentre principalement sur les façons dont vous pouvez potentiellement améliorer les performances spécifiques d’Entity Framework dans ASP.NET. Les suggestions ici sont utiles si vous déterminez que l’accès aux données est l’un des goulots d’étranglement des performances dans votre application. Sauf indication contraire, les méthodes expliquées ici ne doivent pas être considérées comme des « meilleures pratiques » en général : bon nombre d’entre elles ne conviennent que dans des situations exceptionnelles ou pour traiter des types très spécifiques de goulots d’étranglement des performances.

Pour démarrer le tutoriel, démarrez Visual Studio et ouvrez l’application web Contoso University que vous avez travaillée dans le tutoriel précédent.

Entity Framework peut charger des données associées dans les propriétés de navigation d’une entité de plusieurs façons :

  • Chargement différé. Quand l’entité est lue pour la première fois, les données associées ne sont pas récupérées. Toutefois, la première fois que vous essayez d’accéder à une propriété de navigation, les données requises pour cette propriété de navigation sont récupérées automatiquement. Cela entraîne l’envoi de plusieurs requêtes à la base de données : une pour l’entité elle-même et une autre à chaque fois que les données associées pour l’entité doivent être récupérées.

    Image05

Chargement hâtif. Quand l’entité est lue, ses données associées sont également récupérées. Cela génère en général une requête de jointure unique qui récupère toutes les données nécessaires. Vous spécifiez un chargement rapide à l’aide de la Include méthode , comme vous l’avez déjà vu dans ces tutoriels.

Image07

  • Chargement explicite. Cela est similaire au chargement différé, sauf que vous récupérez explicitement les données associées dans le code ; cela ne se produit pas automatiquement lorsque vous accédez à une propriété de navigation. Vous chargez manuellement les données associées à l’aide de la Load méthode de la propriété de navigation pour les collections, ou vous utilisez la Load méthode de la propriété de référence pour les propriétés qui contiennent un seul objet. (Par exemple, vous appelez la PersonReference.Load méthode pour charger la Person propriété de navigation d’une Department entité.)

    Image06

Étant donné qu’ils ne récupèrent pas immédiatement les valeurs de propriété, le chargement différé et le chargement explicite sont également appelés chargement différé.

Le chargement différé est le comportement par défaut d’un contexte d’objet qui a été généré par le concepteur. Si vous ouvrez schoolModel.Designer. Fichier cs qui définit la classe de contexte d’objet, vous trouverez trois méthodes de constructeur et chacune d’elles inclut l’instruction suivante :

this.ContextOptions.LazyLoadingEnabled = true;

En général, si vous savez que vous avez besoin de données associées pour chaque entité récupérée, le chargement rapide offre les meilleures performances, car une requête unique envoyée à la base de données est généralement plus efficace que des requêtes distinctes pour chaque entité récupérée. En revanche, si vous devez accéder aux propriétés de navigation d’une entité uniquement rarement ou uniquement pour un petit ensemble d’entités, le chargement différé ou le chargement explicite peuvent être plus efficaces, car le chargement rapide récupérerait plus de données que nécessaire.

Dans une application web, le chargement différé peut de toute façon avoir une valeur relativement faible, car les actions de l’utilisateur qui affectent le besoin de données associées ont lieu dans le navigateur, qui n’a aucune connexion au contexte d’objet qui a rendu la page. En revanche, lorsque vous reliez des données à un contrôle, vous savez généralement quelles données vous avez besoin. Il est donc généralement préférable de choisir un chargement rapide ou un chargement différé en fonction de ce qui est approprié dans chaque scénario.

En outre, un contrôle de trafic de données peut utiliser un objet d’entité après la suppression du contexte de l’objet. Dans ce cas, une tentative de chargement différé d’une propriété de navigation échouerait. Le message d’erreur que vous recevez est clair : «The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. »

Le EntityDataSource contrôle désactive le chargement différé par défaut. Pour le ObjectDataSource contrôle que vous utilisez pour le didacticiel actuel (ou si vous accédez au contexte de l’objet à partir du code de page), vous pouvez désactiver le chargement différé de plusieurs façons par défaut. Vous pouvez le désactiver lorsque vous instanciez un contexte d’objet. Par exemple, vous pouvez ajouter la ligne suivante à la méthode de constructeur de la SchoolRepository classe :

context.ContextOptions.LazyLoadingEnabled = false;

Pour l’application Contoso University, vous allez faire en sorte que le contexte de l’objet désactive automatiquement le chargement différé afin que cette propriété n’ait pas à être définie chaque fois qu’un contexte est instancié.

Ouvrez le modèle de données SchoolModel.edmx , cliquez sur l’aire de conception, puis dans le volet propriétés, définissez la propriété Lazy Loading Enabled sur False. Enregistrez et fermez le modèle de données.

Image04

Gestion de l’état d’affichage

Pour fournir une fonctionnalité de mise à jour, une page web ASP.NET doit stocker les valeurs de propriété d’origine d’une entité lorsqu’une page est rendue. Pendant le traitement de la publication, le contrôle peut recréer l’état d’origine de l’entité et appeler la méthode de l’entité avant d’appliquer Attach des modifications et d’appeler la SaveChanges méthode . Par défaut, ASP.NET Web Forms contrôles de données utilisent l’état d’affichage pour stocker les valeurs d’origine. Toutefois, l’état d’affichage peut affecter les performances, car il est stocké dans des champs masqués qui peuvent augmenter considérablement la taille de la page envoyée vers et depuis le navigateur.

Les techniques de gestion de l’état d’affichage, ou les alternatives telles que l’état de session, ne sont pas propres à Entity Framework. Ce tutoriel n’aborde donc pas cette rubrique en détail. Pour plus d’informations, consultez les liens à la fin du didacticiel.

Toutefois, la version 4 de ASP.NET offre une nouvelle façon de travailler avec l’état d’affichage que chaque ASP.NET développeur d’applications Web Forms doit connaître : la ViewStateMode propriété . Cette nouvelle propriété peut être définie au niveau de la page ou du contrôle, et vous permet de désactiver l’état d’affichage par défaut pour une page et de l’activer uniquement pour les contrôles qui en ont besoin.

Pour les applications où les performances sont essentielles, une bonne pratique consiste à toujours désactiver l’état d’affichage au niveau de la page et à l’activer uniquement pour les contrôles qui en ont besoin. La taille de l’état d’affichage dans les pages Contoso University ne serait pas considérablement réduite par cette méthode, mais pour voir comment elle fonctionne, vous allez le faire pour la page Instructors.aspx . Cette page contient de nombreux contrôles, notamment un contrôle dont l’état Label d’affichage est désactivé. Aucun des contrôles de cette page n’a réellement besoin d’avoir l’état d’affichage activé. (La DataKeyNames propriété du contrôle spécifie l’état GridView qui doit être conservé entre les publications, mais ces valeurs sont conservées dans l’état de contrôle, ce qui n’est pas affecté par la ViewStateMode propriété.)

La Page directive et Label le balisage de contrôle ressemblent actuellement à l’exemple suivant :

<%@ 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> 
    ...

Apportez les changements suivants :

  • Ajoutez ViewStateMode="Disabled" à la Page directive.
  • Supprimez ViewStateMode="Disabled" du Label contrôle.

Le balisage ressemble maintenant à l’exemple suivant :

<%@ 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> 
    ...

L’état d’affichage est désormais désactivé pour tous les contrôles. Si vous ajoutez ultérieurement un contrôle qui doit utiliser l’état d’affichage, il vous suffit d’inclure l’attribut ViewStateMode="Enabled" pour ce contrôle.

Utilisation de l’option de fusion NoTracking

Lorsqu’un contexte d’objet récupère des lignes de base de données et crée des objets d’entité qui les représentent, par défaut, il effectue également le suivi de ces objets d’entité à l’aide de son gestionnaire d’état d’objet. Ces données de suivi jouent le rôle de cache et sont utilisées lorsque vous mettez à jour une entité. Étant donné qu’une application web a généralement des instances de contexte d’objet de courte durée, les requêtes retournent souvent des données qui n’ont pas besoin d’être suivies, car le contexte d’objet qui les lit est supprimé avant qu’aucune des entités lues ne soit réutilisée ou mise à jour.

Dans Entity Framework, vous pouvez spécifier si le contexte de l’objet suit les objets d’entité en définissant une option de fusion. Vous pouvez définir l’option de fusion pour des requêtes individuelles ou des jeux d’entités. Si vous la définissez pour un jeu d’entités, cela signifie que vous définissez l’option de fusion par défaut pour toutes les requêtes créées pour ce jeu d’entités.

Pour l’application Contoso University, le suivi n’est pas nécessaire pour les jeux d’entités auxquels vous accédez à partir du dépôt. Vous pouvez donc définir l’option NoTracking de fusion sur pour ces jeux d’entités lorsque vous instanciez le contexte d’objet dans la classe du référentiel. (Notez que dans ce tutoriel, la définition de l’option de fusion n’aura pas d’effet notable sur les performances de l’application. L’option NoTracking est susceptible d’améliorer les performances observables uniquement dans certains scénarios de volume de données élevé.)

Dans le dossier DAL, ouvrez le fichier SchoolRepository.cs et ajoutez une méthode de constructeur qui définit l’option de fusion pour les jeux d’entités auxquels le dépôt accède :

public SchoolRepository()
{
    context.Departments.MergeOption = MergeOption.NoTracking;
    context.InstructorNames.MergeOption = MergeOption.NoTracking;
    context.OfficeAssignments.MergeOption = MergeOption.NoTracking;
}

Pré-compilation de requêtes LINQ

La première fois qu’Entity Framework exécute une requête Entity SQL pendant la durée de vie d’un instance donné ObjectContext , la compilation de la requête prend un certain temps. Le résultat de la compilation est mis en cache, ce qui signifie que les exécutions suivantes de la requête sont beaucoup plus rapides. Les requêtes LINQ suivent un modèle similaire, sauf qu’une partie du travail nécessaire à la compilation de la requête est effectuée chaque fois que la requête est exécutée. En d’autres termes, pour les requêtes LINQ, par défaut, tous les résultats de la compilation ne sont pas mis en cache.

Si vous avez une requête LINQ que vous prévoyez d’exécuter à plusieurs reprises dans le contexte d’un objet, vous pouvez écrire du code qui met en cache tous les résultats de la compilation lors de la première exécution de la requête LINQ.

À titre d’illustration, vous allez le faire pour deux Get méthodes de la SchoolRepository classe , dont l’une ne prend aucun paramètre (la GetInstructorNames méthode ) et l’autre qui nécessite un paramètre (la GetDepartmentsByAdministrator méthode ). Ces méthodes telles qu’elles sont actuellement n’ont pas besoin d’être compilées, car il ne s’agit pas de requêtes 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();
}

Toutefois, afin de pouvoir essayer les requêtes compilées, vous allez continuer comme si celles-ci avaient été écrites en tant que requêtes LINQ suivantes :

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();
}

Vous pouvez remplacer le code de ces méthodes par ce qui est indiqué ci-dessus et exécuter l’application pour vérifier qu’elle fonctionne avant de continuer. Mais les instructions suivantes vous permettent de créer des versions précompilées de celles-ci.

Créez un fichier de classe dans le dossier DAL , nommez-le SchoolEntities.cs et remplacez le code existant par le code suivant :

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();
        }
    }
}

Ce code crée une classe partielle qui étend la classe de contexte d’objet générée automatiquement. La classe partielle comprend deux requêtes LINQ compilées à l’aide de la Compile méthode de la CompiledQuery classe . Il crée également des méthodes que vous pouvez utiliser pour appeler les requêtes. Enregistrez et fermez ce fichier.

Ensuite, dans SchoolRepository.cs, modifiez les méthodes et GetDepartmentsByAdministrator existantes GetInstructorNames dans la classe du dépôt afin qu’elles appellent les requêtes compilées :

public IEnumerable<InstructorName> GetInstructorNames()
{
    return context.CompiledInstructorNamesQuery();
}
public IEnumerable<Department> GetDepartmentsByAdministrator(Int32 administrator)
{
    return context.CompiledDepartmentsByAdministratorQuery(administrator);
}

Exécutez la page Departments.aspx pour vérifier qu’elle fonctionne comme avant. La GetInstructorNames méthode est appelée afin de remplir la liste déroulante administrateur, et la GetDepartmentsByAdministrator méthode est appelée lorsque vous cliquez sur Mettre à jour afin de vérifier qu’aucun instructeur n’est administrateur de plusieurs services.

Image03

Vous avez précompilé des requêtes dans l’application Contoso University uniquement pour voir comment procéder, pas parce que cela améliorerait de façon mesurable les performances. La pré-compilation des requêtes LINQ ajoute un niveau de complexité à votre code. Veillez donc à le faire uniquement pour les requêtes qui représentent réellement des goulots d’étranglement de performances dans votre application.

Examen des requêtes envoyées à la base de données

Lorsque vous examinez des problèmes de performances, il est parfois utile de connaître les commandes SQL exactes que Entity Framework envoie à la base de données. Si vous utilisez un IQueryable objet, vous pouvez utiliser la ToTraceString méthode .

Dans SchoolRepository.cs, modifiez le code de la GetDepartmentsByName méthode pour qu’il corresponde à l’exemple suivant :

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();
}

La departments variable doit être convertie en type ObjectQuery uniquement parce que la Where méthode à la fin de la ligne précédente crée un IQueryable objet ; sans la Where méthode, la conversion ne serait pas nécessaire.

Définissez un point d’arrêt sur la return ligne, puis exécutez la page Departments.aspx dans le débogueur. Lorsque vous atteignez le point d’arrêt, examinez la commandText variable dans la fenêtre Variables locales et utilisez le visualiseur de texte (la loupe dans la colonne Valeur ) pour afficher sa valeur dans la fenêtre Visualiseur de texte . Vous pouvez voir l’intégralité de la commande SQL qui résulte de ce code :

Image08

En guise d’alternative, la fonctionnalité IntelliTrace dans Visual Studio Ultimate permet d’afficher les commandes SQL générées par Entity Framework qui ne vous oblige pas à modifier votre code ou même à définir un point d’arrêt.

Remarque

Vous pouvez effectuer les procédures suivantes uniquement si vous avez Visual Studio Ultimate.

Restaurez le code d’origine dans la GetDepartmentsByName méthode, puis exécutez la page Departments.aspx dans le débogueur.

Dans Visual Studio, sélectionnez le menu Déboguer , Puis IntelliTrace, puis Événements IntelliTrace.

Image11

Dans la fenêtre IntelliTrace , cliquez sur Arrêter tout.

Image12

La fenêtre IntelliTrace affiche une liste des événements récents :

Image09

Cliquez sur la ligne ADO.NET . Il se développe pour afficher le texte de la commande :

Image10

Vous pouvez copier l’intégralité de la chaîne de texte de la commande dans le Presse-papiers à partir de la fenêtre Locals .

Supposons que vous travailliez avec une base de données avec plus de tables, de relations et de colonnes que la base de données simple School . Vous constaterez peut-être qu’une requête qui rassemble toutes les informations dont vous avez besoin dans une seule Select instruction contenant plusieurs Join clauses devient trop complexe pour fonctionner efficacement. Dans ce cas, vous pouvez passer d’un chargement hâtif à un chargement explicite pour simplifier la requête.

Par exemple, essayez de modifier le code dans la GetDepartmentsByName méthode dans SchoolRepository.cs. Actuellement dans cette méthode, vous avez une requête d’objet qui contient Include des méthodes pour les propriétés de Person navigation et Courses . Remplacez l’instruction par du return code qui effectue un chargement explicite, comme illustré dans l’exemple suivant :

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;
}

Exécutez la page Departments.aspx dans le débogueur et case activée à nouveau la fenêtre IntelliTrace comme vous l’avez fait précédemment. Maintenant, là où il y avait une seule requête auparavant, vous voyez une longue séquence d’entre elles.

Image13

Cliquez sur la première ligne ADO.NET pour voir ce qui est arrivé à la requête complexe que vous avez consultée précédemment.

Image14

La requête de Department est devenue une requête simple Select sans Join clause, mais elle est suivie de requêtes distinctes qui récupèrent des cours associés et d’un administrateur, à l’aide d’un ensemble de deux requêtes pour chaque service retourné par la requête d’origine.

Remarque

Si vous laissez le chargement différé activé, le modèle que vous voyez ici, avec la même requête répétée plusieurs fois, peut résulter d’un chargement différé. Un modèle que vous souhaitez généralement éviter est le chargement différé des données associées pour chaque ligne de la table primaire. Sauf si vous avez vérifié qu’une requête de jointure unique est trop complexe pour être efficace, vous pouvez généralement améliorer les performances dans ce cas en modifiant la requête principale pour utiliser le chargement rapide.

Pré-génération de vues

Lorsqu’un ObjectContext objet est créé pour la première fois dans un domaine d’application, Entity Framework génère un ensemble de classes qu’il utilise pour accéder à la base de données. Ces classes sont appelées vues, et si vous avez un modèle de données très volumineux, la génération de ces vues peut retarder la réponse du site web à la première demande d’une page après l’initialisation d’un nouveau domaine d’application. Vous pouvez réduire ce délai de première demande en créant les vues au moment de la compilation plutôt qu’au moment de l’exécution.

Remarque

Si votre application n’a pas de modèle de données extrêmement volumineux, ou si elle a un modèle de données volumineux, mais que vous n’êtes pas préoccupé par un problème de performances qui affecte uniquement la toute première demande de page après le recyclage d’IIS, vous pouvez ignorer cette section. La création d’une vue ne se produit pas chaque fois que vous instanciez un ObjectContext objet, car les vues sont mises en cache dans le domaine d’application. Par conséquent, à moins que vous ne recycliez fréquemment votre application dans IIS, très peu de demandes de page bénéficieraient des vues générées au préalable.

Vous pouvez prégéner des vues à l’aide de l’outil en ligne de commandeEdmGen.exeou à l’aide d’un modèle T4 ( Text Template Transformation Toolkit ). Dans ce tutoriel, vous allez utiliser un modèle T4.

Dans le dossier DAL , ajoutez un fichier à l’aide du modèle Modèle de texte (il se trouve sous le nœud Général dans la liste Modèles installés ) et nommez-le SchoolModel.Views.tt. Remplacez le code existant dans le fichier par le code suivant :

<#
/***************************************************************************

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;
    }
#>

Ce code génère des vues pour un fichier .edmx qui se trouve dans le même dossier que le modèle et qui porte le même nom que le fichier de modèle. Par exemple, si votre fichier de modèle est nommé SchoolModel.Views.tt, il recherche un fichier de modèle de données nommé SchoolModel.edmx.

Enregistrez le fichier, cliquez avec le bouton droit sur le fichier dans Explorateur de solutions et sélectionnez Exécuter l’outil personnalisé.

Image02

Visual Studio génère un fichier de code qui crée les vues, qui est nommé SchoolModel.Views.cs en fonction du modèle. (Vous avez peut-être remarqué que le fichier de code est généré avant même de sélectionner Exécuter l’outil personnalisé, dès que vous enregistrez le fichier de modèle.)

Image01

Vous pouvez maintenant exécuter l’application et vérifier qu’elle fonctionne comme avant.

Pour plus d’informations sur les vues prégénées, consultez les ressources suivantes :

Ceci termine l’introduction à l’amélioration des performances dans une application web ASP.NET qui utilise Entity Framework. Pour plus d’informations, consultez les ressources suivantes :

Le tutoriel suivant passe en revue certaines des améliorations importantes apportées à Entity Framework qui sont nouvelles dans la version 4.