Partilhar via


Considerações sobre desempenho (Entity Framework)

Este tópico descreve as características de desempenho do ADO.NET Entity Framework e fornece algumas considerações para ajudar a melhorar o desempenho dos aplicativos do Entity Framework.

Etapas da execução da consulta

Para entender melhor o desempenho das consultas no Entity Framework, é útil entender as operações que ocorrem quando uma consulta é executada em um modelo conceitual e retorna dados como objetos. A tabela a seguir descreve essa série de operações.

Operação Custo relativo Frequência Comentários
Carregando metadados Moderado Uma vez em cada domínio de aplicação. Os metadados de modelo e mapeamento usados pelo Entity Framework são carregados em um MetadataWorkspacearquivo . Esses metadados são armazenados em cache globalmente e estão disponíveis para outras instâncias do mesmo domínio de ObjectContext aplicativo.
Abrindo a conexão do banco de dados Moderado1 Conforme necessário. Como uma conexão aberta com o banco de dados consome um recurso valioso, o Entity Framework abre e fecha a conexão do banco de dados somente conforme necessário. Você também pode abrir explicitamente a conexão. Para obter mais informações, consulte Gerenciando conexões e transações.
Geração de visualizações Alto Uma vez em cada domínio de aplicação. (Pode ser pré-gerado.) Antes que o Entity Framework possa executar uma consulta em um modelo conceitual ou salvar alterações na fonte de dados, ele deve gerar um conjunto de exibições de consulta locais para acessar o banco de dados. Devido ao alto custo de gerar essas visualizações, você pode pré-gerar as visualizações e adicioná-las ao projeto em tempo de design. Para obter mais informações, consulte Como pré-gerar exibições para melhorar o desempenho da consulta.
Preparar a consulta Moderado2 Uma vez para cada consulta exclusiva. Inclui os custos para compor o comando de consulta, gerar uma árvore de comandos com base em metadados de modelo e mapeamento e definir a forma dos dados retornados. Como agora os comandos de consulta do Entity SQL e as consultas LINQ são armazenados em cache, as execuções posteriores da mesma consulta levam menos tempo. Você ainda pode usar consultas LINQ compiladas para reduzir esse custo em execuções posteriores e as consultas compiladas podem ser mais eficientes do que as consultas LINQ que são armazenadas automaticamente em cache. Para obter mais informações, consulte Consultas compiladas (LINQ to Entities). Para obter informações gerais sobre a execução de consultas LINQ, consulte LINQ to Entities. Nota: As consultas LINQ to Entities que aplicam o Enumerable.Contains operador a coleções na memória não são armazenadas automaticamente em cache. Também não é permitida parametrizar coleções na memória em consultas LINQ compiladas.
Executando a consulta Baixa2 Uma vez para cada consulta. O custo de execução do comando na fonte de dados usando o provedor de dados ADO.NET. Como a maioria das fontes de dados armazena planos de consulta em cache, execuções posteriores da mesma consulta podem levar ainda menos tempo.
Carregando e validando tipos Baixa3 Uma vez para cada ObjectContext instância. Os tipos são carregados e validados em relação aos tipos que o modelo conceitual define.
Monitorização Baixa3 Uma vez para cada objeto retornado por uma consulta. 4 Se uma consulta usar a opção de mesclagem, esse estágio não afetará o NoTracking desempenho.

Se a consulta usar a AppendOnlyopção , PreserveChangesou OverwriteChanges mesclar, os ObjectStateManagerresultados da consulta serão rastreados no . Um EntityKey é gerado para cada objeto controlado que a consulta retorna e é usado para criar um ObjectStateEntry no ObjectStateManager. Se um existente ObjectStateEntry puder ser encontrado para o EntityKey, o objeto existente será retornado. Se a PreserveChangesopção , ou OverwriteChanges for usada, o objeto será atualizado antes de ser retornado.

Para obter mais informações, consulte Resolução de identidade, Gerenciamento de estado e Controle de alterações.
Materialização dos objetos Moderado3 Uma vez para cada objeto retornado por uma consulta. 4 O processo de leitura do objeto retornado DbDataReader e criação de objetos e definição de valores de propriedade que são baseados nos valores em cada instância da DbDataRecord classe. Se o objeto já existir no e a consulta usar as opções ou PreserveChanges mesclar, esse estágio não afetará o ObjectContext AppendOnly desempenho. Para obter mais informações, consulte Resolução de identidade, Gerenciamento de estado e Controle de alterações.

1 Quando um provedor de fonte de dados implementa o pool de conexões, o custo de abrir uma conexão é distribuído pelo pool. O Provedor .NET para SQL Server oferece suporte ao pool de conexões.

2 O custo aumenta com o aumento da complexidade da consulta.

3 O custo total aumenta proporcionalmente ao número de objetos retornados pela consulta.

4 Essa sobrecarga não é necessária para consultas EntityClient porque as consultas EntityClient retornam um EntityDataReader em vez de objetos. Para obter mais informações, consulte EntityClient Provider for the Entity Framework.

Considerações adicionais

A seguir estão outras considerações que podem afetar o desempenho de aplicativos do Entity Framework.

Execução de Consultas

Como as consultas podem exigir muitos recursos, considere em que ponto do seu código e em que computador uma consulta é executada.

Execução diferida versus execução imediata

Quando você cria uma ObjectQuery<T> consulta LINQ ou LINQ, a consulta pode não ser executada imediatamente. A execução da consulta é adiada até que os resultados sejam necessários, como durante uma foreach enumeração (C#) ou For Each (Visual Basic) ou quando ela é atribuída para preencher uma List<T> coleção. A execução da consulta começa imediatamente quando você chama o Execute método em um ObjectQuery<T> ou quando você chama um método LINQ que retorna uma consulta singleton, como First ou Any. Para obter mais informações, consulte Consultas de objeto e execução de consultas (LINQ to Entities).

Execução do lado do cliente de consultas LINQ

Embora a execução de uma consulta LINQ ocorra no computador que hospeda a fonte de dados, algumas partes de uma consulta LINQ podem ser avaliadas no computador cliente. Para obter mais informações, consulte a seção Execução de armazenamento de Execução de consulta (LINQ to Entities).

Complexidade de consulta e mapeamento

A complexidade das consultas individuais e do mapeamento no modelo de entidade terá um efeito significativo no desempenho da consulta.

Complexidade do mapeamento

Modelos que são mais complexos do que um simples mapeamento um-para-um entre entidades no modelo conceitual e tabelas no modelo de armazenamento geram comandos mais complexos do que modelos que têm um mapeamento um-para-um.

Complexidade da consulta

As consultas que exigem um grande número de junções nos comandos que são executados na fonte de dados ou que retornam uma grande quantidade de dados podem afetar o desempenho das seguintes maneiras:

  • Consultas em um modelo conceitual que parecem simples podem resultar na execução de consultas mais complexas na fonte de dados. Isso pode ocorrer porque o Entity Framework converte uma consulta em um modelo conceitual em uma consulta equivalente em relação à fonte de dados. Quando um único conjunto de entidades no modelo conceitual mapeia para mais de uma tabela na fonte de dados ou quando uma relação entre entidades é mapeada para uma tabela de junção, o comando de consulta executado em relação à consulta da fonte de dados pode exigir uma ou mais junções.

    Nota

    Use o ToTraceString método das classes ou EntityCommand para exibir os comandos que são executados na fonte de ObjectQuery<T> dados de uma determinada consulta. Para obter mais informações, consulte Como exibir os comandos da loja.

  • As consultas SQL de entidade aninhada podem criar junções no servidor e podem retornar um grande número de linhas.

    Segue-se um exemplo de uma consulta aninhada numa cláusula de projeção:

    SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c  ) As Inner2
        FROM AdventureWorksModel.JobCandidate AS c  ) As Inner1
        FROM AdventureWorksModel.EmployeeDepartmentHistory AS c  
    

    Além disso, essas consultas fazem com que o pipeline de consulta gere uma única consulta com duplicação de objetos em consultas aninhadas. Devido a isso, uma única coluna pode ser duplicada várias vezes. Em alguns bancos de dados, incluindo o SQL Server, isso pode fazer com que a tabela TempDB cresça muito, o que pode diminuir o desempenho do servidor. Deve-se ter cuidado ao executar consultas aninhadas.

  • Qualquer consulta que retorne uma grande quantidade de dados pode causar diminuição do desempenho se o cliente estiver executando operações que consomem recursos de forma proporcional ao tamanho do conjunto de resultados. Nesses casos, você deve considerar limitar a quantidade de dados retornados pela consulta. Para obter mais informações, consulte Como percorrer os resultados da consulta.

Quaisquer comandos gerados automaticamente pelo Entity Framework podem ser mais complexos do que comandos semelhantes escritos explicitamente por um desenvolvedor de banco de dados. Se você precisar de controle explícito sobre os comandos executados em sua fonte de dados, considere definir um mapeamento para uma função com valor de tabela ou procedimento armazenado.

Relações

Para um desempenho de consulta ideal, você deve definir relações entre entidades como associações no modelo de entidade e como relações lógicas na fonte de dados.

Caminhos de consulta

Por padrão, quando você executa um ObjectQuery<T>, os objetos relacionados não são retornados (embora os objetos que representam as próprias relações o sejam). Você pode carregar objetos relacionados de três maneiras:

  1. Defina o caminho da consulta antes que o ObjectQuery<T> seja executado.

  2. Chame o Load método na propriedade de navegação que o objeto expõe.

  3. Defina a LazyLoadingEnabled opção no ObjectContext para true. Observe que isso é feito automaticamente quando você gera código de camada de objeto com o Entity Data Model Designer. Para obter mais informações, consulte Visão geral do código gerado.

Ao considerar qual opção usar, lembre-se de que há uma compensação entre o número de solicitações no banco de dados e a quantidade de dados retornados em uma única consulta. Para obter mais informações, consulte Carregando objetos relacionados.

Usando caminhos de consulta

Os caminhos de consulta definem o gráfico de objetos que uma consulta retorna. Quando você define um caminho de consulta, apenas uma única solicitação no banco de dados é necessária para retornar todos os objetos que o caminho define. O uso de caminhos de consulta pode resultar em comandos complexos sendo executados na fonte de dados a partir de consultas de objetos aparentemente simples. Isso ocorre porque uma ou mais junções são necessárias para retornar objetos relacionados em uma única consulta. Essa complexidade é maior em consultas em um modelo de entidade complexo, como uma entidade com herança ou um caminho que inclui relações muitos-para-muitos.

Nota

Use o ToTraceString método para ver o comando que será gerado por um ObjectQuery<T>arquivo . Para obter mais informações, consulte Como exibir os comandos da loja.

Quando um caminho de consulta inclui muitos objetos relacionados ou os objetos contêm muitos dados de linha, a fonte de dados pode não conseguir concluir a consulta. Isso ocorre se a consulta exigir armazenamento temporário intermediário que exceda os recursos da fonte de dados. Quando isso ocorre, você pode reduzir a complexidade da consulta da fonte de dados carregando explicitamente objetos relacionados.

Você pode carregar explicitamente objetos relacionados chamando o Load método em uma propriedade de navegação que retorna um EntityCollection<TEntity> ou EntityReference<TEntity>. O carregamento explícito de objetos requer uma viagem de ida e volta ao banco de dados toda vez Load que é chamado.

Nota

se você chamar Load durante o looping através de uma coleção de objetos retornados, como quando você usa a foreach instrução (For Each no Visual Basic), o provedor específico da fonte de dados deve oferecer suporte a vários conjuntos de resultados ativos em uma única conexão. Para um banco de dados do SQL Server, você deve especificar um valor de na cadeia de conexão do MultipleActiveResultSets = true provedor.

Você também pode usar o LoadProperty método quando não EntityCollection<TEntity> há propriedades ou EntityReference<TEntity> em entidades. Isso é útil quando você está usando entidades POCO.

Embora o carregamento explícito de objetos relacionados reduza o número de junções e a quantidade de dados redundantes, Load requer conexões repetidas com o banco de dados, o que pode se tornar caro ao carregar explicitamente um grande número de objetos.

Guardar alterações

Quando você chama o SaveChanges método em um ObjectContext, um comando separado criar, atualizar ou excluir é gerado para cada objeto adicionado, atualizado ou excluído no contexto. Esses comandos são executados na fonte de dados em uma única transação. Assim como nas consultas, o desempenho das operações de criação, atualização e exclusão depende da complexidade do mapeamento no modelo conceitual.

Transações distribuídas

As operações em uma transação explícita que exigem recursos gerenciados pelo coordenador de transações distribuídas (DTC) serão muito mais caras do que uma operação semelhante que não requer o DTC. A promoção ao DTC ocorrerá nas seguintes situações:

  • Uma transação explícita com uma operação em um banco de dados do SQL Server 2000 ou outra fonte de dados que sempre promove transações explícitas para o DTC.

  • Uma transação explícita com uma operação no SQL Server 2005 quando a conexão é gerenciada pelo Entity Framework. Isso ocorre porque o SQL Server 2005 promove para um DTC sempre que uma conexão é fechada e reaberta em uma única transação, que é o comportamento padrão do Entity Framework. Essa promoção DTC não ocorre ao usar o SQL Server 2008. Para evitar essa promoção ao usar o SQL Server 2005, você deve abrir e fechar explicitamente a conexão dentro da transação. Para obter mais informações, consulte Gerenciando conexões e transações.

Uma transação explícita é usada quando uma ou mais operações são executadas dentro de uma System.Transactions transação. Para obter mais informações, consulte Gerenciando conexões e transações.

Estratégias para melhorar o desempenho

Você pode melhorar o desempenho geral das consultas no Entity Framework usando as estratégias a seguir.

Pré-gerar visualizações

A geração de exibições com base em um modelo de entidade é um custo significativo na primeira vez que um aplicativo executa uma consulta. Use o utilitário EdmGen.exe para pré-gerar exibições como um arquivo de código Visual Basic ou C# que pode ser adicionado ao projeto durante o design. Você também pode usar o Text Template Transformation Toolkit para gerar exibições pré-compiladas. As exibições pré-geradas são validadas em tempo de execução para garantir que sejam consistentes com a versão atual do modelo de entidade especificado. Para obter mais informações, consulte Como pré-gerar exibições para melhorar o desempenho da consulta.

Ao trabalhar com modelos muito grandes, aplica-se a seguinte consideração:

O formato de metadados .NET limita o número de caracteres de cadeia de caracteres do usuário em um determinado binário para 16.777.215 (0xFFFFFF). Se você estiver gerando visualizações para um modelo muito grande e o arquivo de exibição atingir esse limite de tamanho, você obterá o erro de compilação "Sem espaço lógico para criar mais cadeias de caracteres de usuário". Essa limitação de tamanho se aplica a todos os binários gerenciados. Para obter mais informações, consulte o blog que demonstra como evitar o erro ao trabalhar com modelos grandes e complexos.

Considere usar a opção de mesclagem NoTracking para consultas

Há um custo necessário para rastrear objetos retornados no contexto do objeto. Detetar alterações em objetos e garantir que várias solicitações para a mesma entidade lógica retornem a mesma instância de objeto requer que os objetos sejam anexados a uma ObjectContext instância. Se você não planeja fazer atualizações ou exclusões em objetos e não requer gerenciamento de identidade, considere usar as opções de mesclagem NoTracking ao executar consultas.

Retornar a quantidade correta de dados

Em alguns cenários, especificar um caminho de consulta usando o Include método é muito mais rápido porque requer menos viagens de ida e volta ao banco de dados. No entanto, em outros cenários, viagens de ida e volta adicionais ao banco de dados para carregar objetos relacionados podem ser mais rápidas porque as consultas mais simples com menos junções resultam em menos redundância de dados. Por isso, recomendamos que você teste o desempenho de várias maneiras de recuperar objetos relacionados. Para obter mais informações, consulte Carregando objetos relacionados.

Para evitar retornar muitos dados em uma única consulta, considere paginar os resultados da consulta em grupos mais gerenciáveis. Para obter mais informações, consulte Como percorrer os resultados da consulta.

Limitar o escopo do ObjectContext

Na maioria dos casos, você deve criar uma ObjectContext instância dentro de uma using instrução (Using…End Using no Visual Basic). Isso pode aumentar o desempenho, garantindo que os recursos associados ao contexto do objeto sejam descartados automaticamente quando o código sair do bloco de instrução. No entanto, quando os controles são vinculados a objetos gerenciados pelo contexto do objeto, a ObjectContext instância deve ser mantida enquanto a associação for necessária e descartada manualmente. Para obter mais informações, consulte Gerenciando conexões e transações.

Considere abrir a conexão do banco de dados manualmente

Quando seu aplicativo executa uma série de consultas de objeto ou chamadas SaveChanges freqüentes para persistir criar, atualizar e excluir operações para a fonte de dados, o Entity Framework deve abrir e fechar continuamente a conexão com a fonte de dados. Nessas situações, considere abrir manualmente a conexão no início dessas operações e fechar ou descartar a conexão quando as operações forem concluídas. Para obter mais informações, consulte Gerenciando conexões e transações.

Dados de Desempenho

Alguns dados de desempenho para o Entity Framework são publicados nas seguintes postagens no blog da equipe do ADO.NET:

Consulte também