Maximizando o desempenho com o Entity Framework 4.0 em um aplicativo Web ASP.NET 4

por Tom Dykstra

Esta série de tutoriais se baseia no aplicativo Web da Contoso University criado pelo Introdução com a série de tutoriais do Entity Framework 4.0. Se você não concluiu os tutoriais anteriores, como ponto de partida para este tutorial, poderá baixar o aplicativo que teria criado. Você também pode baixar o aplicativo criado pela série de tutoriais completa. Se você tiver dúvidas sobre os tutoriais, poderá postá-los no fórum ASP.NET Entity Framework.

No tutorial anterior, você viu como lidar com conflitos de simultaneidade. Este tutorial mostra opções para melhorar o desempenho de um aplicativo Web ASP.NET que usa o Entity Framework. Você aprenderá vários métodos para maximizar o desempenho ou diagnosticar problemas de desempenho.

As informações apresentadas nas seções a seguir provavelmente serão úteis em uma ampla variedade de cenários:

  • Carregue dados relacionados com eficiência.
  • Gerenciar o estado de exibição.

As informações apresentadas nas seções a seguir podem ser úteis se você tiver consultas individuais que apresentam problemas de desempenho:

  • Use a opção NoTracking de mesclagem.
  • Pré-compilar consultas LINQ.
  • Examine os comandos de consulta enviados para o banco de dados.

As informações apresentadas na seção a seguir são potencialmente úteis para aplicativos que têm modelos de dados extremamente grandes:

  • Pré-gerar exibições.

Observação

O desempenho do aplicativo Web é afetado por muitos fatores, incluindo coisas como o tamanho dos dados de solicitação e resposta, a velocidade das consultas de banco de dados, quantas solicitações o servidor pode enfileirar e a rapidez com que ele pode a service-los e até mesmo a eficiência de qualquer biblioteca cliente-script que você possa estar usando. Se o desempenho for crítico em seu aplicativo ou se o teste ou a experiência mostrar que o desempenho do aplicativo não é satisfatório, você deverá seguir o protocolo normal para ajuste de desempenho. Medida para determinar onde os gargalos de desempenho estão ocorrendo e, em seguida, abordar as áreas que terão maior impacto no desempenho geral do aplicativo.

Este tópico se concentra principalmente em maneiras pelas quais você pode potencialmente melhorar o desempenho especificamente do Entity Framework em ASP.NET. As sugestões aqui serão úteis se você determinar que o acesso a dados é um dos gargalos de desempenho em seu aplicativo. Exceto como observado, os métodos explicados aqui não devem ser considerados "práticas recomendadas" em geral — muitas delas são apropriadas apenas em situações excepcionais ou para lidar com tipos muito específicos de gargalos de desempenho.

Para iniciar o tutorial, inicie o Visual Studio e abra o aplicativo Web da Contoso University com o qual você estava trabalhando no tutorial anterior.

Há várias maneiras pelas quais o Entity Framework pode carregar dados relacionados nas propriedades de navegação de uma entidade:

  • Carregamento lento. Quando a entidade é lida pela primeira vez, os dados relacionados não são recuperados. No entanto, na primeira vez que você tenta acessar uma propriedade de navegação, os dados necessários para essa propriedade de navegação são recuperados automaticamente. Isso resulta em várias consultas enviadas para o banco de dados — uma para a própria entidade e outra para cada vez que os dados relacionados para a entidade devem ser recuperados.

    Imagem05

Carregamento adiantado. Quando a entidade é lida, os dados relacionados são recuperados com ela. Normalmente, isso resulta em uma única consulta de junção que recupera todos os dados necessários. Especifique o carregamento ansioso usando o Include método , como já viu nestes tutoriais.

Imagem07

  • Carregamento explícito. Isso é semelhante ao carregamento lento, exceto que você recupera explicitamente os dados relacionados no código; isso não acontece automaticamente quando você acessa uma propriedade de navegação. Você carrega dados relacionados manualmente usando o Load método da propriedade de navegação para coleções ou usa o Load método da propriedade de referência para propriedades que contêm um único objeto. (Por exemplo, você chama o PersonReference.Load método para carregar a Person propriedade de navegação de uma Department entidade.)

    Image06

Como eles não recuperam imediatamente os valores de propriedade, o carregamento lento e o carregamento explícito também são conhecidos como carregamento adiado.

Carregamento lento é o comportamento padrão para um contexto de objeto que foi gerado pelo designer. Se você abrir o SchoolModel.Designer. arquivo cs que define a classe de contexto de objeto, você encontrará três métodos de construtor e cada um deles inclui a seguinte instrução:

this.ContextOptions.LazyLoadingEnabled = true;

Em geral, se você souber que precisa de dados relacionados para cada entidade recuperada, o carregamento ansioso oferece o melhor desempenho, pois uma única consulta enviada ao banco de dados normalmente é mais eficiente do que consultas separadas para cada entidade recuperada. Por outro lado, se você precisar acessar as propriedades de navegação de uma entidade apenas com pouca frequência ou apenas para um pequeno conjunto de entidades, o carregamento lento ou explícito pode ser mais eficiente, pois o carregamento ansioso recuperaria mais dados do que você precisa.

Em um aplicativo Web, o carregamento lento pode ter um valor relativamente pequeno de qualquer maneira, pois as ações do usuário que afetam a necessidade de dados relacionados ocorrem no navegador, que não tem nenhuma conexão com o contexto do objeto que renderizou a página. Por outro lado, ao associar dados a um controle, você normalmente sabe quais dados precisa e, portanto, geralmente é melhor escolher carregamento ansioso ou carregamento adiado com base no que é apropriado em cada cenário.

Além disso, um controle de entrada de dados pode usar um objeto de entidade após o contexto do objeto ser descartado. Nesse caso, uma tentativa de carregar uma propriedade de navegação lenta falharia. A mensagem de erro recebida é clara: "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."

O EntityDataSource controle desabilita o carregamento lento por padrão. Para o ObjectDataSource controle que você está usando para o tutorial atual (ou se você acessar o contexto do objeto do código da página), há várias maneiras de tornar o carregamento lento desabilitado por padrão. Você pode desabilitá-lo ao instanciar um contexto de objeto. Por exemplo, você pode adicionar a seguinte linha ao método de construtor da SchoolRepository classe :

context.ContextOptions.LazyLoadingEnabled = false;

Para o aplicativo da Contoso University, você fará com que o contexto do objeto desabilite automaticamente o carregamento lento para que essa propriedade não precise ser definida sempre que um contexto for instanciado.

Abra o modelo de dados SchoolModel.edmx , clique na superfície de design e, no painel de propriedades, defina a propriedade Lazy Loading Enabled comoFalse. Salve e feche o modelo de dados.

Imagem04

Gerenciando o estado de exibição

Para fornecer a funcionalidade de atualização, uma página da Web ASP.NET deve armazenar os valores de propriedade originais de uma entidade quando uma página é renderizada. Durante o processamento de postback, o controle pode recriar o estado original da entidade e chamar o método da Attach entidade antes de aplicar alterações e chamar o SaveChanges método . Por padrão, ASP.NET Web Forms controles de dados usam o estado de exibição para armazenar os valores originais. No entanto, o estado de exibição pode afetar o desempenho, pois ele é armazenado em campos ocultos que podem aumentar substancialmente o tamanho da página enviada de e para o navegador.

As técnicas para gerenciar o estado de exibição ou alternativas, como o estado da sessão, não são exclusivas do Entity Framework, portanto, este tutorial não entra neste tópico em detalhes. Para obter mais informações, consulte os links no final do tutorial.

No entanto, a versão 4 do ASP.NET fornece uma nova maneira de trabalhar com o estado de exibição que cada desenvolvedor ASP.NET de aplicativos Web Forms deve estar ciente: a ViewStateMode propriedade . Essa nova propriedade pode ser definida no nível de página ou controle e permite desabilitar o estado de exibição por padrão para uma página e habilitá-la somente para controles que precisam dela.

Para aplicativos em que o desempenho é crítico, uma boa prática é sempre desabilitar o estado de exibição no nível da página e habilitá-lo apenas para controles que o exigem. O tamanho do estado de exibição nas páginas da Contoso University não seria substancialmente reduzido por esse método, mas para ver como ele funciona, você o fará para a página Instructors.aspx . Essa página contém muitos controles, incluindo um Label controle que tem o estado de exibição desabilitado. Nenhum dos controles nesta página realmente precisa ter o estado de exibição habilitado. (A DataKeyNames propriedade do controle especifica o GridView estado que deve ser mantido entre postbacks, mas esses valores são mantidos no estado de controle, que não é afetado pela ViewStateMode propriedade .)

A Page diretiva e Label a marcação de controle atualmente se assemelham ao seguinte exemplo:

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

Faça as seguintes alterações:

  • Adicione ViewStateMode="Disabled" à Page diretiva .
  • Remova ViewStateMode="Disabled" do Label controle.

A marcação agora se assemelha ao seguinte exemplo:

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

O estado de exibição agora está desabilitado para todos os controles. Se você adicionar mais tarde um controle que precisa usar o estado de exibição, tudo o que você precisa fazer é incluir o ViewStateMode="Enabled" atributo para esse controle.

Usando a opção de mesclagem NoTracking

Quando um contexto de objeto recupera linhas de banco de dados e cria objetos de entidade que as representam, por padrão, ele também rastreia esses objetos de entidade usando seu gerenciador de estado de objeto. Esses dados de acompanhamento atuam como um cache e são usados quando você atualiza uma entidade. Como um aplicativo Web normalmente tem instâncias de contexto de objeto de curta duração, as consultas geralmente retornam dados que não precisam ser rastreados, pois o contexto de objeto que as lê será descartado antes que qualquer uma das entidades que ele lê seja usada novamente ou atualizada.

No Entity Framework, você pode especificar se o contexto do objeto rastreia objetos de entidade definindo uma opção de mesclagem. Você pode definir a opção de mesclagem para consultas individuais ou para conjuntos de entidades. Se você defini-lo para um conjunto de entidades, isso significa que você está definindo a opção de mesclagem padrão para todas as consultas criadas para esse conjunto de entidades.

Para o aplicativo da Contoso University, o acompanhamento não é necessário para nenhum dos conjuntos de entidades que você acessa do repositório, para que você possa definir a opção de mesclagem para NoTracking esses conjuntos de entidades ao instanciar o contexto de objeto na classe de repositório. (Observe que, neste tutorial, definir a opção de mesclagem não terá um efeito perceptível no desempenho do aplicativo. É NoTracking provável que a opção faça uma melhoria de desempenho observável apenas em determinados cenários de alto volume de dados.)

Na pasta DAL, abra o arquivo SchoolRepository.cs e adicione um método de construtor que define a opção de mesclagem para os conjuntos de entidades que o repositório acessa:

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

Pré-compilando consultas LINQ

Na primeira vez que o Entity Framework executa uma consulta Entity SQL na vida útil de uma determinada ObjectContext instância, leva algum tempo para compilar a consulta. O resultado da compilação é armazenado em cache, o que significa que as execuções subsequentes da consulta são muito mais rápidas. As consultas LINQ seguem um padrão semelhante, exceto que parte do trabalho necessário para compilar a consulta é feito sempre que a consulta é executada. Em outras palavras, para consultas LINQ, por padrão nem todos os resultados da compilação são armazenados em cache.

Se você tiver uma consulta LINQ que espera executar repetidamente na vida útil de um contexto de objeto, poderá escrever um código que faça com que todos os resultados da compilação sejam armazenados em cache na primeira vez que a consulta LINQ for executada.

Como ilustração, você fará isso para dois Get métodos na SchoolRepository classe , um dos quais não usa parâmetros (o GetInstructorNames método ) e um que requer um parâmetro (o GetDepartmentsByAdministrator método ). Esses métodos como estão agora, na verdade, não precisam ser compilados porque não são consultas 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();
}

No entanto, para que você possa experimentar consultas compiladas, prossiga como se elas tivessem sido escritas como as seguintes consultas LINQ:

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

Você pode alterar o código nesses métodos para o que é mostrado acima e executar o aplicativo para verificar se ele funciona antes de continuar. No entanto, as instruções a seguir entram diretamente na criação de versões pré-compiladas delas.

Crie um arquivo de classe na pasta DAL , nomeie-o SchoolEntities.cs e substitua o código existente pelo seguinte código:

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

Esse código cria uma classe parcial que estende a classe de contexto de objeto gerada automaticamente. A classe parcial inclui duas consultas LINQ compiladas usando o Compile método da CompiledQuery classe . Ele também cria métodos que você pode usar para chamar as consultas. Salve e feche este arquivo.

Em seguida, em SchoolRepository.cs, altere os métodos e GetDepartmentsByAdministrator existentes GetInstructorNames na classe do repositório para que eles chamem as consultas compiladas:

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

Execute a página Departments.aspx para verificar se ela funciona como antes. O GetInstructorNames método é chamado para preencher a lista suspensa de administradores e o GetDepartmentsByAdministrator método é chamado quando você clica em Atualizar para verificar se nenhum instrutor é um administrador de mais de um departamento.

Imagem03

Você compilou previamente consultas no aplicativo da Contoso University apenas para ver como fazer isso, não porque isso melhoraria o desempenho mensuravelmente. A pré-compilação de consultas LINQ adiciona um nível de complexidade ao seu código, portanto, certifique-se de fazê-lo apenas para consultas que realmente representam gargalos de desempenho em seu aplicativo.

Examinando consultas enviadas para o banco de dados

Quando você está investigando problemas de desempenho, às vezes é útil saber os comandos SQL exatos que o Entity Framework está enviando para o banco de dados. Se você estiver trabalhando com um IQueryable objeto , uma maneira de fazer isso é usar o ToTraceString método .

Em SchoolRepository.cs, altere o código no GetDepartmentsByName método para corresponder ao seguinte exemplo:

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

A departments variável deve ser convertida em um ObjectQuery tipo somente porque o Where método no final da linha anterior cria um IQueryable objeto ; sem o Where método , a conversão não seria necessária.

Defina um ponto de interrupção na return linha e execute a página Departments.aspx no depurador. Ao atingir o ponto de interrupção, examine a commandText variável na janela Locais e use o visualizador de texto (a lupa na coluna Valor ) para exibir seu valor na janela Visualizador de Texto . Você pode ver todo o comando SQL resultante deste código:

Imagem08

Como alternativa, o recurso IntelliTrace no Visual Studio Ultimate fornece uma maneira de exibir comandos SQL gerados pelo Entity Framework que não exigem que você altere seu código ou até mesmo defina um ponto de interrupção.

Observação

Você só poderá executar os procedimentos a seguir se tiver Visual Studio Ultimate.

Restaure o código original no GetDepartmentsByName método e execute a página Departments.aspx no depurador.

No Visual Studio, selecione o menu Depurar , IntelliTrace e, em seguida, Eventos do IntelliTrace.

Image11

Na janela do IntelliTrace , clique em Quebrar Tudo.

Imagem12

A janela do IntelliTrace exibe uma lista de eventos recentes:

Imagem09

Clique na linha ADO.NET . Ele se expande para mostrar o texto do comando:

Imagem10

Você pode copiar toda a cadeia de caracteres de texto do comando para a área de transferência da janela Locais.

Suponha que você estava trabalhando com um banco de dados com mais tabelas, relações e colunas do que o banco de dados simples School . Você pode descobrir que uma consulta que coleta todas as informações necessárias em uma única Select instrução que contém várias Join cláusulas se torna muito complexa para funcionar com eficiência. Nesse caso, você pode alternar do carregamento adiantado para o carregamento explícito para simplificar a consulta.

Por exemplo, tente alterar o código no GetDepartmentsByName método em SchoolRepository.cs. Atualmente, nesse método, você tem uma consulta de objeto que tem Include métodos para as Person propriedades de navegação e Courses . Substitua a instrução return pelo código que executa o carregamento explícito, conforme mostrado no exemplo a seguir:

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

Execute a página Departments.aspx no depurador e marcar a janela do IntelliTrace novamente como você fez antes. Agora, onde havia uma única consulta antes, você vê uma longa sequência delas.

Imagem13

Clique na primeira linha ADO.NET para ver o que aconteceu com a consulta complexa exibida anteriormente.

Imagem14

A consulta de Departamentos tornou-se uma consulta simples Select sem Join cláusula, mas é seguida por consultas separadas que recuperam cursos relacionados e um administrador, usando um conjunto de duas consultas para cada departamento retornado pela consulta original.

Observação

Se você deixar o carregamento lento habilitado, o padrão que você vê aqui, com a mesma consulta repetida várias vezes, poderá resultar do carregamento lento. Um padrão que você normalmente deseja evitar é o carregamento lento de dados relacionados para cada linha da tabela primária. A menos que você tenha verificado que uma única consulta de junção é muito complexa para ser eficiente, você normalmente poderá melhorar o desempenho nesses casos alterando a consulta primária para usar o carregamento adiantado.

Pré-geração de exibições

Quando um ObjectContext objeto é criado pela primeira vez em um novo domínio de aplicativo, o Entity Framework gera um conjunto de classes que ele usa para acessar o banco de dados. Essas classes são chamadas de exibições e, se você tiver um modelo de dados muito grande, gerar essas exibições poderá atrasar a resposta do site à primeira solicitação de uma página depois que um novo domínio de aplicativo for inicializado. Você pode reduzir esse atraso de primeira solicitação criando as exibições em tempo de compilação em vez de em tempo de execução.

Observação

Se o aplicativo não tiver um modelo de dados extremamente grande ou se ele tiver um modelo de dados grande, mas você não estiver preocupado com um problema de desempenho que afete apenas a primeira solicitação de página depois que o IIS for reciclado, você poderá ignorar esta seção. A criação da exibição não acontece toda vez que você cria uma instância de um ObjectContext objeto, pois as exibições são armazenadas em cache no domínio do aplicativo. Portanto, a menos que você esteja reciclando frequentemente seu aplicativo no IIS, pouquíssimas solicitações de página se beneficiariam de exibições pré-geradas.

Você pode pré-gerar exibições usando a ferramenta de linha de comando EdmGen.exe ou usando um modelo T4 ( Text Template Transformation Toolkit ). Neste tutorial, você usará um modelo T4.

Na pasta DAL , adicione um arquivo usando o modelo Modelo de Texto (ele está sob o nó Geral na lista Modelos Instalados ) e nomeie-o como SchoolModel.Views.tt. Substitua o código existente no arquivo pelo seguinte código:

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

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

Esse código gera exibições para um arquivo .edmx localizado na mesma pasta que o modelo e que tem o mesmo nome que o arquivo de modelo. Por exemplo, se o arquivo de modelo for nomeado SchoolModel.Views.tt, ele procurará um arquivo de modelo de dados chamado SchoolModel.edmx.

Salve o arquivo e clique com o botão direito do mouse no arquivo no Gerenciador de Soluções e selecione Executar Ferramenta Personalizada.

Imagem02

O Visual Studio gera um arquivo de código que cria as exibições, que é denominada SchoolModel.Views.cs com base no modelo. (Talvez você tenha notado que o arquivo de código é gerado antes mesmo de selecionar Executar Ferramenta Personalizada, assim que salvar o arquivo de modelo.)

Image01

Agora você pode executar o aplicativo e verificar se ele funciona como antes.

Para obter mais informações sobre exibições pré-geradas, consulte os seguintes recursos:

Isso conclui a introdução à melhoria do desempenho em um aplicativo Web ASP.NET que usa o Entity Framework. Para saber mais, consulte os recursos a seguir:

O próximo tutorial examina alguns dos aprimoramentos importantes para o Entity Framework que são novos na versão 4.