Compartilhar via


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

por Tom Dykstra

Esta série de tutoriais baseia-se no aplicativo Web da Contoso University criado pela série de tutoriais Getting Started with the 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 ao 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 itens 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 acioná-los e até mesmo a eficiência de qualquer biblioteca de script de cliente que você possa estar usando. Se o desempenho for crítico em seu aplicativo ou se o teste ou a experiência mostrarem que o desempenho do aplicativo não é satisfatório, você deverá seguir o protocolo normal para ajuste de desempenho. Medida para determinar onde estão ocorrendo gargalos de desempenho e, em seguida, abordar as áreas que terão 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 sã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 abordar 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 cada vez que os dados relacionados para a entidade devem ser recuperados.

    Imagem 05

Carregamento antecipado Quando a entidade é lida, os dados relacionados são recuperados junto 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 método Include, como você já viu nesses 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 método PersonReference.Load para carregar a propriedade de navegação Person de uma entidade Department.)

    Image06

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

O carregamento lento é o comportamento padrão de um contexto de objeto que foi gerado pelo designer. Se você abrir o arquivo SchoolModel.Designer.cs que define a classe de contexto de objeto, 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 o carregamento 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 vincular um controle, você normalmente sabe quais dados precisa e, portanto, geralmente é melhor optar pelo carregamento antecipado ou adiado com base no que é apropriado em cada cenário.

Além disso, um controle vinculado a dados pode usar um objeto de entidade após o descarte do contexto do objeto. Nesse caso, uma tentativa de carregar sob demanda uma propriedade de navegação 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 do objeto. Por exemplo, você pode adicionar a seguinte linha ao método 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 como False. Salve e feche o modelo de dados.

Imagem 04

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 original 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 Attach da entidade antes de aplicar alterações e chamar o método SaveChanges. Por padrão, ASP.NET controles de dados do Web Forms 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 exigem isso. 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ê aplicará isso à 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 propriedade DataKeyNames do controle GridView especifica estado que deve ser mantido entre postbacks, mas esses valores mantêm-se no estado de controle, que a propriedade ViewStateMode não afeta.)

A diretiva Page e a marcação de controle Label 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" à diretiva Page.
  • Remova ViewStateMode="Disabled" do controle Label.

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 do 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 que são 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 no repositório, portanto, você pode definir a opção de mesclagem para NoTracking esses conjuntos de entidades ao instanciar o contexto do 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;
}

Compilação prévia de consultas LINQ

Na primeira vez que o Entity Framework executa uma consulta SQL de entidade dentro da 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 é feita 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 faz com que todos os resultados da compilação sejam armazenados em cache na primeira vez em 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 atualmente, realmente 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 existentes GetInstructorNames e GetDepartmentsByAdministrator na classe de 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 do administrador e o GetDepartmentsByAdministrator método é chamado quando você clica em Atualizar para verificar se nenhum instrutor é um administrador de mais de um departamento.

Imagem 03

Você compilou consultas previamente no aplicativo da Contoso University apenas para ver como fazê-lo, não porque isso melhoraria mensuravelmente o desempenho. 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 ao 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 apenas 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:

Imagem 08

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 o Visual Studio Ultimate.

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

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

Image11

Na janela Do IntelliTrace , clique em Interromper Tudo.

Imagem12

A janela IntelliTrace exibe uma lista de eventos recentes:

Imagem 09

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

Image10

Você pode copiar o texto completo do comando para a área de transferência a partir 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 perceber que uma consulta que reúne todas as informações de que você precisa em uma única instrução Select contendo várias cláusulas Join pode se tornar muito complexa para funcionar de maneira eficiente. Nesse caso, você pode mudar do carregamento ansioso 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 possui métodos para as propriedades de navegação Person e Courses. Substitua a return instrução 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 verifique a janela 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

As consultas de Departamentos tornaram-se uma consulta simples Select sem cláusula Join, mas são seguidas por consultas separadas que recuperam cursos relacionados e um administrador, usando 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, normalmente é possível melhorar o desempenho nesses casos alterando a consulta primária para utilizar 'eager loading'.

Visualizações pré-geradas

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 de visualizações não ocorre toda vez que você instancia um objeto ObjectContext, porque as visualizaçõ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 de Texto (ele está no nó Geral na lista Modelos Instalados) e nomeie-o 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 do 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 os modos de exibição, que é nomeado SchoolModel.Views.cs com base no modelo. (Você deve ter notado que o arquivo de código é gerado antes mesmo de selecionar Executar Ferramenta Personalizada, assim que salvar o arquivo de modelo.)

Imagem 01

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 obter mais informações, consulte os seguintes recursos:

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