Este artigo foi traduzido por máquina.

Pontos de dados

Determinando o perfil das atividades de banco de dados no Entity Framework

Julie Lerman

Baixe o código de exemplo

In last month’s Data Points column (msdn.microsoft.com/magazine/gg309181), I wrote about profiling the performance of the Estrutura de Entidade against SQL Azure. Neste mês, analiso outro tipo de perfil — o perfil das consultas — para saber quais consultas e comandos estão sendo executados no banco de dados em resposta a consultas e outras atividades de acesso a dados no Entity Framework.

Um dos principais recursos do Entity Framework é a geração de comandos para executar consultas de banco de dados, bem como inserções, atualizações e exclusões. Essa é uma grande vantagem para muitos desenvolvedores, apesar de que sempre haverá um debate contínuo sobre a qualidade do código SQL gerado por ferramentas de mapeamento relacional de objetos (ORM) versus código SQL criado manualmente por especialistas (não entrarei nesse debate neste artigo). Na maioria das vezes, o SQL gerado é muito bom, principalmente se considerarmos que ele deve ser construído dinamicamente de uma forma genérica, independentemente da criatividade que se consegue com as expressões de consulta LINQ to Entities ou Entity SQL.

Embora tenha sido dada muita atenção ao aprimoramento da geração de comandos no Entity Framework 4, ainda é importante saber o que está acontecendo no banco de dados. A qualidade da consulta de repositório gerada é somente uma parte da história. Talvez você esteja escrevendo código que cria tempos de execução de banco de dados estendidos ou um número incomum de viagens ao banco de dados. Esses são aspectos críticos que devem ser levados em consideração quando se determina o perfil do aplicativo.

Durante os primeiros anos de existência do Entity Framework, não havia nada disponível fora das ferramentas de criação de perfil de banco de dados, como o SQL Profiler, que, embora instrutivo, requer muita configuração e mineração se você deseja exibir os resultados de uma forma fácil de entender. A seguir apresento uma variedade de opções para você criar um perfil de consulta do Entity Framework que vai além das ferramentas de criação de perfil de banco de dados.

O método ObjectContext.ToTraceString do Entity Framework

A API do Entity Framework API (3.5 e 4) tem um único método para inspecionar consultas em tempo de execução, ToTraceString, que é útil, mas só fornece informações sobre um subconjunto de chamadas feitas ao banco de dados. ToTraceString é um método de ObjectQuery, portanto, se você estiver escrevendo uma consulta LINQ to Entities, primeiro deverá converter a consulta em um ObjectQuery antes de chamar ToTraceString. Eis um exemplo:

var query = from c in context.Customers where c.CustomerID == 3 select c;
var objectQuery=query as System.Data.Objects.ObjectQuery;
Console.WriteLine(objectQuery.ToTraceString());

Isso gera a seguinte cadeia de caracteres:

SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[SalesPerson] AS [SalesPerson],
[Extent1].[EmailAddress] AS [EmailAddress],
[Extent1].[Phone] AS [Phone],
[Extent1].[ModifiedDate] AS [ModifiedDate],
[Extent1].[TimeStamp] AS [TimeStamp]
FROM [SalesLT].[Customer] AS [Extent1]
WHERE 3 = [Extent1].[CustomerID]

Observe que o exemplo de código não executa a consulta. Tampouco ToTraceString, o que faz com que a consulta seja processada (transformada em uma consulta de repositório) pelo Entity Framework com a ajuda do provedor de banco de dados System.Data.SqlClient, mas não força a consulta a ser executada no banco de dados.

Você pode usar ToTraceString somente com consultas definidas explicitamente. Portanto, não é possível usá-lo para ver consultas executadas em consequência de carregamento deferido com o método Load ou de carregamento lento. Também não é possível usá-lo para inspecionar atividades como inserções, atualizações, exclusões ou a execução de procedimento armazenado.

Por fim, é importante observar que você não consegue inserir os resultados de ToTraceString facilmente em um processo de depuração, criando, por exemplo, um visualizador de depurador. Isso exigiria que ObjectQuery fosse serializado, o que não é o caso.

Determinação de perfil com o IntelliTrace do Visual Studio 2010

O IntelliTrace está disponível no Visual Studio 2010 Ultimate, mas não nas versões menores. Ele captura a atividade de banco de dados, inclusive a que é acionada pelo Entity Framework, mas não exibe os valores de parâmetro que foram enviados com o comando.

A seguir está parte do código que executa as seguintes tarefas:

  1. Executa uma consulta de 10 clientes
  2. Carrega lentamente os pedidos do primeiro cliente retornado
  3. Desabilita o carregamento lento
  4. Carrega explicitamente os pedidos do segundo cliente retornado
  5. Modifica um cliente
  6. Envia as alterações feitas no banco de dados com o método SaveChanges
  7. Executa uma função que mapeei para um procedimento armazenado em um Modelo de Dados de Entidade
var query = from c in context.Customers select c;
var custList = query.Take(10).ToList();

Customer custFirst = custList[0];
int orderCount = custFirst.Orders.Count; 

context.ContextOptions.LazyLoadingEnabled=false;
Customer custSecond = custList[1];
custSecond.Orders.Load(); 

custSecond.ModifiedDate = DateTime.Now;
context.SaveChanges(); 

ObjectResult<PartialOrderDetails> orders= 
  context.GetCustomerOrdersForId(custList[2].CustomerID);

Quando executado, esse código forçará a execução de três instruções SELECT, uma instrução UPDATE e, depois, um comando Execute referente ao procedimento armazenado no banco de dados.

Observando a captura de tela do IntelliTrace na Figura 1, podemos ver todos esses cinco comandos.

image: A Series of Database Commands Displayed in the Visual Studio IntelliTrace Display

Figura 1 Uma série de comandos de banco de dados exibidos na tela do IntelliTrace do Visual Studio

No entanto, se expandirmos um desses itens, como ilustrado na Figura 2, veremos que o comando está ali, mas os parâmetros não.

Figura 2 Uma instrução Select detalhada coletada pelo recurso IntelliTrace do Visual Studio 2010

Portanto, se deseja ver a atividade de banco de dados, inclusive os parâmetros, você terá de usar algum tipo de criador de perfil externo.

EFTracingProvider na Galeria de código do MSDN

Jarek Kowalski escreveu EFTracingProvider quando fazia parte da equipe do Entity Framework na Microsoft. Existe uma versão para o Microsoft .NET Framework 3.5 e uma para o .NET Framework 4.

Para utilizar o EFTracingProvider, você precisará construir um wrapper em torno da classe ObjectContext, AWEntities, e usá-lo no lugar de AWEntities. Essa classe estendida fornece métodos de rastreamento, como Log, que você pode usar para registrar atividades contextuais em log. Há um exemplo do wrapper de classe necessário fornecido no download de EFTracingProvider. Você também encontrará o código relevante no download deste artigo (code.msdn.microsoft.com/mag201012DataPoints). Além disso, é preciso adicionar duas configurações de DbProviderFactories no arquivo de configuração do aplicativo. Com tudo isso funcionando, você poderá então instanciar o contexto estendido e começar a registrar em log.

Este é um exemplo que cria um arquivo de texto para capturar eventos de log e usa o método TracingProvider.Log para registrar em log todas as atividades:

using (TextWriter logFile = File.CreateText("sqllogfile.txt"))
{
  using (var context = new ExtendedAWEntities())
  {
    context.Log = logFile;
    var query = from c in context.Customers select c;
    var custList = query.Take(10).ToList();
  }
  Console.WriteLine(File.ReadAllText("sqllogfile.txt"));
}

Utilizando o wrapper TracingProvider e a classe de contexto ExtendedAWEntities, reexecutei o mesmo conjunto de código do exemplo anterior do IntelliTrace.

Todos os cinco comandos de banco de dados foram registrados em log, sendo que cada um deles foi registrado com os parâmetros relevantes.

A Figura 3, como exemplo, mostra o comando enviado como resultado do carregamento lento, em que o valor do parâmetro EntityKeyValue1 é especificado depois de o comando ser listado.

Figura 3 Um comando capturado por EFTracingProvider

    SELECT
    [Extent1].[SalesOrderID] AS [SalesOrderID],
    [Extent1].[OrderDate] AS [OrderDate],
    [Extent1].[DueDate] AS [DueDate],
    [Extent1].[OnlineOrderFlag] AS [OnlineOrderFlag],
    [Extent1].[SalesOrderNumber] AS [SalesOrderNumber],
    [Extent1].[PurchaseOrderNumber] AS [PurchaseOrderNumber],
    [Extent1].[AccountNumber] AS [AccountNumber],
    [Extent1].[CustomerID] AS [CustomerID],
    [Extent1].[BillToAddressID] AS [BillToAddressID],
    [Extent1].[CreditCardApprovalCode] AS [CreditCardApprovalCode],
    [Extent1].[SubTotal] AS [SubTotal],
    [Extent1].[Comment] AS [Comment],
    [Extent1].[ModifiedDate] AS [ModifiedDate],
    [Extent1].[ShipDate] AS [ShipDate]
    FROM [SalesLT].[SalesOrderHeader] AS [Extent1]
    WHERE [Extent1].[CustomerID] = @EntityKeyValue1
    -- EntityKeyValue1 (dbtype=Int32, size=0, direction=Input) = 1

EFTracingProvider é simples de implementar e fornece todos os comandos de banco de dados gerados pelo código do Entity Framework como texto não processado.Também é possível inscrever-se para receber eventos de rastreamento: CommandExecuting, CommandFinished e CommandFailed no contexto.Eles dão acesso ao DbCommand bruto antes e depois da execução, assim você pode analisar ou registrar em log os detalhes adicionais.

Você pode baixar o EFTracingProvider — junto com seu complemento, o EFCachingProvider, e uma solução de exemplo, o EFProviderWrapperDemo, que explora todos esses recursos — gratuitamente da Galeria de código do MSDN (code.msdn.microsoft.com/EFProviderWrappers).

Criadores de perfil de terceiros

No entanto, talvez você queira ir além do texto não processado do arquivo de log de EFTracingProvider.Você pode aproveitar e obter informações no código desses arquivos de log ou se beneficiar com duas ferramentas que já fizeram todo o trabalho para você.Existem duas ferramentas de terceiros para criar o perfil de consultas do Entity Framework: o Hibernating Rhinos Entity Framework Profiler e o Huagati Query Profiler.

Além disso, o LINQPad, cujo enfoque é permitir que você teste expressões de consulta fora do seu aplicativo, exibe o SQL das expressões que você está executando.Embora seja uma ferramenta indispensável para qualquer pessoa que escreve consultas LINQ em uma grande variedade de provedores, ela permite criar o perfil das consultas geradas pelo seu aplicativo e, portanto, não a examinarei com mais detalhes nesta coluna.

O Entity Framework Profiler (EF Prof) faz parte da família Hibernating Rhinos UberProf de criadores de perfil (hibernatingrhinos.com/products/UberProf).Também existem criadores de perfil para nHibernate, Hibernate e LINQ to SQL.Um quinto criado de perfil, o LLBLGen Pro, estava em sua versão beta quando escrevi este artigo.O EF Prof combina a propriedade intelectual existente derivada das outras ferramentas UberProf com algumas ideias tiradas de EFTracingProvider.Em sua forma mais simples, é possível adicionar uma única linha de código ao aplicativo para habilitá-lo a conversar com o mecanismo do EF Prof e ter os resultados relatados no aplicativo cliente do EF Prof:

HibernatingRhinos.Profiler.
Appender.EntityFramework.
EntityFrameworkProfiler.Initialize

A atividade de banco de dados é agrupada pela instância de ObjectContext. Na Figura 4, podemos ver que há duas instâncias de ObjectContext sendo exibidas — porque executei meu código de exemplo duas vezes.

image: The EF Prof Query Profiler UI

Figura 4 A interface do usuário do EF Prof Query Profiler

Também na Figura 4, no lado direito, podemos ver uma prévia de cada uma das chamadas de banco de dados para a instância de contexto selecionada. Parece que há uma instrução SELECT adicional sendo chamada após o comando UPDATE. Na verdade, isso faz parte do comando enviado com SaveChanges, uma vez que o Entity Framework assegura que o campo TimeStamp atualizado da linha Customer é retornado para a instância do cliente.

Quando realça uma instrução SQL na interface do usuário, você pode ver todo o SQL na tela inferior junto com uma referência ao fato de que o valor (neste caso, 5) foi passado como parâmetro, @EntityKeyValue1.

O EF Prof também mostra as linhas resultantes da consulta, e até mesmo o plano de consulta de banco de dados. A guia Rastreamento de Pilha, mostrada na Figura 5, mostra como o aplicativo veio a executar um determinado comando e permite até mesmo saltar diretamente para essa linha de código no Visual Studio.

Figura 5 O Rastreamento de Pilha do EF Prof permite saltar para o código que executou o comando de banco de dados selecionado

O EF Prof pode capturar toda a atividade do Entity Framework de um aplicativo e a apresenta em uma interface do usuário de fácil navegação, junto com alguns adereços, como a exibição do plano de consulta, e vincula de volta para o código de execução. Uma licença padrão do EF Prof custa US$ 305 com desconto para várias licenças e um plano de assinatura. O EF Prof trabalha com qualquer um dos provedores de dados do Entity Framework e, portanto, não está limitado ao SQL Server. Ele funciona com o .NET Framework versões 3.5 e 4.

Huagati Query Profiler, originally called L2S Profiler, was updated in November to add support for the Estrutura de Entidade 4. You can also use it to profile LINQ para SQL and LLBLGen Pro, but it currently only works with SQL Server.

Implementar o Query Profiler envolve fazer referência ao assembly do criador de perfil (Huagati.EFProfiler.dll) no seu aplicativo e adicionar dois novos construtores, mais uma certa lógica adicional para a classe ObjectContext em uma classe parcial. A Figura 6 mostra a classe parcial que criei para a minha classe AWEntities.

Figura 6 A classe parcial a ser usada com o Huagati Query Profiler

string profilerOutput =
      System.IO.Path.Combine(System.Environment.GetFolderPath(
        Environment.SpecialFolder.Personal),
      @"EFProfiler\Samples");
    _profiler=new HuagatiEFProfiler.EFProfiler(this, profilerOutput, null, 
      HuagatiEFProfiler.ExecutionPlanMode.Actual, false);
   _profiler.LogError += EFProfiler_LogError;
  }
}

O método EFProfiler.GetConnection se conecta ao banco de dados para rastrear sua atividade. No site da Huagati, você obtém mais informações sobre diversas configurações que podem ser usadas para instanciar o EFProfiler.

O criador de perfil coleta seus dados e os coloca em um arquivo na pasta designada. Você pode abrir esse arquivo no Log Explorer do criador de perfil, mostrado na Figura 7.

Figura 7 A interface do usuário do Huagati Entity Framework Query Profiler

Como você pode ver na Figura 7, todas as cinco solicitações de banco de dados foram coletadas. O comando Update é combinado ao respectivo comando SELECT para retornar TimeStamp, que é exatamente como o comando é enviado para o banco de dados.

O Log Explorer mostrado na Figura 7 exibe as linhas relevantes dos dados do SQL Profiler do SQL Server. Assim como no EF Prof, você pode ver a consulta com seus parâmetros, vincular novamente às linhas de código relevantes no aplicativo usando a exibição Pilha, exibir o plano de execução e ver algumas estatísticas sobre as consultas.

A atividade de banco de dados relativa a cada instância de contexto é armazenada em um arquivo de log separado, assim o Log Explorer exibirá somente um conjunto de comandos de cada vez. As configurações permitem criar alertas codificados por cor que destaquem níveis de atividade pouco comuns, como tempos de execução visivelmente longos ou até mesmo alarmantes.

A interface do usuário do Query Profiler não é tão atraente quanto a do EF Prof, e é preciso fazer um investimento um pouco maior no código (adicionando lógica a cada ObjectContext do seu aplicativo). Mas os componentes são distribuíveis, ou seja, você pode coletar informações do criador de perfil dos aplicativos em execução no ambiente do seu cliente. Also, it doesn’t have as many analysis options as EF Prof. But the $20 Standard and $40 Professional sticker price (which includes all three profilers), may make up for these differences for many developers. Lembre-se de que o Huagati Entity Framework Profiler ainda estava na versão beta quando fiz minha sondagem e que ele só funciona com o SQL Server, ao contrário do EF Prof, que funciona com qualquer um dos Provedores de Dados disponíveis do ADO.NET compatíveis com o Entity Framework.

Uma introdução ao suporte para Entity Framework da Huagati está disponível em tinyurl.com/26cfful. Ao final dessa postagem de blog você encontrará um link para baixar a versão beta, 1.31.

A ferramenta certa para o trabalho

Sou uma grande defensora de se usar a ferramenta certa para cada trabalho e que é um desperdício tentar obter funcionalidade do Visual Studio 2010 quando existem outras excelentes ferramentas disponíveis. Nesta coluna, você viu uma série de ferramentas integradas às APIs do Entity Framework e ao Visual Studio 2010, uma extensão que fornecerá dados brutos e duas ferramentas de terceiros que executam não somente a tarefa de coleta de dados, mas também a apresentação. Seja qual for a ferramenta que você escolher, mesmo que simplesmente use o SQL Profiler, não tome seu banco de dados por certo ao gerar o perfil do seu aplicativo.

Julie Lerman é uma Microsoft MVP, mentora e consultora do .NET, que reside nas colinas de Vermont. Você pode encontrar sua apresentação sobre acesso a dados e outros tópicos do Microsoft .NET em grupos de usuários e conferências ao redor do mundo. Seu blog está em thedatafarm.com/blog e ela é autora do livro altamente reconhecido, “Programming Entity Framework” (O’Reilly Media, 2010). Siga-a no Twitter.com: julielerman.

Meus agradecimentos aos seguinte especialista técnico para revisar este artigo: Jarek Kowalski