Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
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 de aplicativos do Entity Framework.
Estágios de execução de 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 esta série de operações.
| Operação | Custo Relativo | Frequência | Comentários |
|---|---|---|---|
| Carregando metadados | Moderado | Uma vez em cada domínio do aplicativo. | Metadados de modelo e mapeamento usados pela Estrutura de Entidade são carregados em um MetadataWorkspace. 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 de 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 de 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. |
| Gerando exibições | Alto | Uma vez em cada domínio do aplicativo. (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 local para acessar o banco de dados. Devido ao alto custo de geração dessas exibições, você pode pré-gerar as exibiçõ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. |
| Preparando 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 SQL da entidade e as consultas LINQ são armazenados em cache, 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 consultas compiladas podem ser mais eficientes do que as consultas LINQ armazenadas automaticamente em cache. Para obter mais informações, consulte Consultas Compiladas (LINQ to Entities). Para obter informações gerais sobre a execução da consulta 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 é permitido parametrizar coleções na memória em consultas LINQ compiladas. |
| Executando a consulta | Baixo2 | Uma vez para cada consulta. | O custo de executar o comando na fonte de dados usando o provedor de dados ADO.NET. Como a maioria das fontes de dados armazena em cache planos de consulta, execuções posteriores da mesma consulta podem levar ainda menos tempo. |
| Carregando e validando tipos | Baixo3 | Uma vez para cada ObjectContext instância. | Os tipos são carregados e validados contra os tipos definidos pelo modelo conceitual. |
| Acompanhamento | Baixo3 | Uma vez para cada objeto que uma consulta retorna. 4 | Se uma consulta usar a opção NoTracking de mesclagem, esse estágio não afetará o desempenho. Se a consulta usa a opção de mesclagem AppendOnly, PreserveChanges ou OverwriteChanges, os resultados da consulta são controlados no ObjectStateManager. Um EntityKey é gerado para cada objeto rastreado que a consulta retorna e é usado para criar um ObjectStateEntry no ObjectStateManager. Se um ObjectStateEntry existente puder ser encontrado para o EntityKey, o objeto existente será retornado. Se PreserveChanges ou a opção 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. |
| Materializando os objetos | Moderado3 | Uma vez para cada objeto que uma consulta retorna. 4 | O processo de ler o objeto retornado DbDataReader e criar objetos e definir valores de propriedade que são baseados nos valores em cada instância da DbDataRecord classe. Se o objeto já existir no ObjectContext e a consulta usar as opções de mesclagem AppendOnly ou PreserveChanges, esse estágio não afetará o 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 dá suporte ao pool de conexões.
2 Aumentos de custo com maior complexidade de 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 para o Entity Framework.
Considerações adicionais
Veja a seguir outras considerações que podem afetar o desempenho de aplicativos do Entity Framework.
Execução da consulta
Como as consultas podem ser intensivas em recursos, considere em que ponto em seu código e em qual computador uma consulta é executada.
Execução adiada versus imediata
Quando você cria uma ObjectQuery<T> consulta 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 consulta (LINQ to Entities).
Execução de consultas LINQ do lado do cliente
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 da 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 de mapeamento
Modelos mais complexos do que um mapeamento um-para-um simples 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
Consultas que exigem um grande número de junções nos comandos 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 na fonte de dados. Quando uma única entidade definida no modelo conceitual é mapeada 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 na consulta da fonte de dados pode exigir uma ou mais junções.
Observação
Use o método ToTraceString das classes ObjectQuery<T> ou EntityCommand para exibir os comandos que são executados na fonte de dados para uma determinada consulta. Para obter mais informações, consulte Como exibir os comandos do repositório.
Consultas SQL de entidades aninhadas podem criar junções no servidor e retornar um grande número de linhas.
Veja a seguir um exemplo de uma consulta aninhada em uma 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 cAlém disso, essas consultas fazem o pipeline de consulta gerar uma única consulta com duplicação de objetos nas consultas aninhadas. Por 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 grande, o que pode diminuir o desempenho do servidor. Tome cuidado ao executar consultas aninhadas.
Todas as consultas que retornam uma grande quantidade de dados podem causar um desempenho reduzido 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: navegar pelos resultados da consulta.
Todos os comandos gerados automaticamente pelo Entity Framework podem ser mais complexos do que comandos semelhantes gravados explicitamente por um desenvolvedor de banco de dados. Se você precisar de controle explícito sobre os comandos executados na fonte de dados, considere definir um mapeamento para uma função com valor de tabela ou procedimento armazenado.
Relações
Para obter um desempenho de consulta ideal, você deve definir relações entre entidades, tanto como associações no modelo de entidade quanto como relações lógicas na fonte de dados.
Caminhos de consulta
Por padrão, quando você executa um ObjectQuery<T>, objetos relacionados não são retornados (embora objetos que representam as próprias relações sejam). Você pode carregar objetos relacionados de uma das três maneiras:
Defina o caminho da consulta antes de ObjectQuery<T> ser executado.
Chame o método
Loadna propriedade de navegação que é exposta pelo objeto.Defina a opção LazyLoadingEnabled no ObjectContext para
true. Observe que isso é feito automaticamente quando você gera código de camada de objeto com o Designer de Modelo de Dados de Entidade. 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 grafo 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 definidos pelo caminho. O uso de caminhos de consulta pode resultar na execução de comandos complexos contra a fonte de dados a partir de consultas a objetos aparentemente simples. Isso ocorre porque uma ou mais junções são necessárias para retornar objetos relacionados em uma única consulta. Esta complexidade é maior em consultas em um modelo complexo de entidade, como uma entidade com herança ou um caminho que inclui relações muitos-para-muitos.
Observação
Use o ToTraceString método para ver o comando que será gerado por um ObjectQuery<T>. Para obter mais informações, consulte Como exibir os comandos do repositório.
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 ocorrerá se a consulta exigir um 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.
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>. Carregar objetos explicitamente exige uma viagem de ida e volta ao banco de dados toda vez que Load é chamado.
Observação
Se você chamar Load enquanto está executando um loop em uma coleção de objetos retornados, como quando você usar a instrução foreach (For Each no Visual Basic), o provedor específico da fonte de dados deverá dar 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 MultipleActiveResultSets = true na string de conexão do provedor.
Você também pode usar o LoadProperty método quando não houver nenhuma EntityCollection<TEntity> ou EntityReference<TEntity> propriedades em entidades. Isso é útil quando você está usando entidades POCO.
Embora o carregamento explícito de objetos relacionados reduza explicitamente o número de junções e reduza a quantidade de dados redundantes, Load requer conexões repetidas com o banco de dados, que podem se tornar dispendiosas ao carregar explicitamente um grande número de objetos.
Salvando alterações
Quando você chama o método SaveChanges em um ObjectContext, um comando separado de criação, atualização ou exclusão é 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 acontece com as 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 DTC (coordenador de transações distribuídas) serão muito mais caras do que uma operação semelhante que não exige o DTC. A promoção para o 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 promova transações explícitas para o DTC.
Uma transação explícita envolvendo uma operação no SQL Server 2005 quando a conexão é gerenciada pelo Entity Framework. Isso ocorre porque o SQL Server 2005 promove 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 de 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
Gerar 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 do Visual Basic ou C# que pode ser adicionado ao projeto durante o design. Você também pode usar a Ferramenta de Transformação de Modelos de Texto para gerar visualizações pré-compiladas. As exibições pré-geradas são validadas em runtime 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, a seguinte consideração se aplica:
O formato de metadados do .NET limita o número de caracteres de cadeia de caracteres de usuário em um determinado binário a 16.777.215 (0xFFFFFF). Se você estiver gerando exibições para um modelo muito grande e o arquivo de exibição atingir esse limite de tamanho, você receberá o erro de compilação "Não há 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.
Use a opção de mesclagem NoTracking para consultas
Há um custo necessário para rastrear objetos retornados no contexto do objeto. Detectar 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 precisar de gerenciamento de identidade, considere usar as NoTracking opções de mesclagem 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 para o 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 o retorno de muitos dados em uma única consulta, considere paginar os resultados da consulta em grupos mais gerenciáveis. Para obter mais informações, consulte Como: navegar pelos 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 associados a objetos gerenciados pelo contexto do objeto, a ObjectContext instância deve ser mantida desde que a associação seja necessária e descartada manualmente. Para obter mais informações, consulte Gerenciando conexões e transações.
Considere abrir a conexão de banco de dados manualmente
Quando seu aplicativo executa uma série de consultas de objeto ou chamadas SaveChanges frequentes para persistir operações de criação, atualização e exclusão 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 do Entity Framework são publicados nas seguintes postagens no blog da equipe ADO.NET:
Explorando o desempenho do ADO.NET Entity Framework – Parte 1
Explorando o desempenho do ADO.NET Entity Framework – Parte 2