Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Como regra geral, o Entity Framework Core tenta avaliar uma consulta no servidor tanto quanto possível. O EF Core converte partes da consulta em parâmetros, que podem ser avaliados no lado do cliente. O restante da consulta (juntamente com os parâmetros gerados) é fornecido ao provedor de banco de dados para determinar a consulta de banco de dados equivalente a ser avaliada no servidor. O EF Core suporta a avaliação parcial por parte do cliente na projeção de nível superior, essencialmente, na última invocação de Select()
. Se a projeção de nível superior na consulta não puder ser convertida para o servidor, o EF Core buscará todos os dados necessários do servidor e avaliará as partes restantes da consulta no cliente. Se o EF Core detetar uma expressão, em qualquer lugar que não seja a projeção de nível superior, que não pode ser traduzida para o servidor, ele lançará uma exceção de tempo de execução. Consulte Como as consultas funcionam para entender como o EF Core determina o que não pode ser traduzido para o servidor.
Observação
Antes da versão 3.0, o Entity Framework Core suportava a avaliação do cliente em qualquer lugar da consulta. Para obter mais informações, consulte a seção de versões anteriores.
Sugestão
Você pode visualizar a amostra do deste artigo no GitHub.
Avaliação do cliente na projeção de nível superior
No exemplo a seguir, um método auxiliar é usado para padronizar URLs para blogs, que são retornados de um banco de dados do SQL Server. Como o provedor do SQL Server não tem informações sobre como esse método é implementado, não é possível traduzi-lo em SQL. Todos os outros aspetos da consulta são avaliados na base de dados, mas passar o retornado URL
por este método é feito no cliente.
var blogs = await context.Blogs
.OrderByDescending(blog => blog.Rating)
.Select(
blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
.ToListAsync();
public static string StandardizeUrl(string url)
{
url = url.ToLower();
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", url);
}
return url;
}
Avaliação de cliente não suportada
Embora a avaliação do cliente seja útil, às vezes pode resultar em um desempenho ruim. Considere a consulta a seguir, na qual o método auxiliar agora é usado num filtro 'where'. Como o filtro não pode ser aplicado no banco de dados, todos os dados precisam ser puxados para a memória para aplicar o filtro no cliente. Com base no filtro e na quantidade de dados no servidor, a avaliação do cliente pode resultar em um desempenho ruim. Portanto, o Entity Framework Core bloqueia essa avaliação do cliente e lança uma exceção de tempo de execução.
var blogs = await context.Blogs
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Avaliação explícita do cliente
Pode ser necessário forçar explicitamente a avaliação do cliente em certos casos, como o seguinte:
- A quantidade de dados é pequena para que a avaliação no cliente não cause um grande impacto no desempenho.
- O operador LINQ que está sendo usado não tem tradução do lado do servidor.
Nesses casos, você pode optar explicitamente pela avaliação do cliente chamando métodos como AsEnumerable
ou ToList
(AsAsyncEnumerable
ou ToListAsync
para assíncrono). Ao usar AsEnumerable
você estaria transmitindo os resultados, mas usar ToList
causaria buffering criando uma lista, que também leva memória adicional. No entanto, se você estiver enumerando várias vezes, o armazenamento de resultados em uma lista ajuda mais, pois há apenas uma consulta ao banco de dados. Dependendo do uso específico, você deve avaliar qual método é mais útil para o caso.
var blogs = context.Blogs
.AsAsyncEnumerable()
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToListAsync();
Sugestão
Se você estiver usando AsAsyncEnumerable
e quiser compor a consulta mais no lado do cliente, então você pode usar a biblioteca System.Interactive.Async, que define operadores para enumeráveis assíncronos. Para mais informações, consulte os operadores LINQ do lado do cliente.
Potencial fuga de memória na avaliação do cliente
Como a tradução e a compilação de consultas são caras, o EF Core armazena em cache o plano de consulta compilado. O delegado armazenado em cache pode usar o código do cliente durante a avaliação do cliente na projeção de nível superior. O EF Core gera parâmetros para as partes da árvore avaliadas pelo cliente e reutiliza o plano de consulta substituindo os valores dos parâmetros. Mas certas constantes na árvore de expressão não podem ser convertidas em parâmetros. Se o delegado armazenado em cache contiver essas constantes, esses objetos não poderão ser coletados como lixo, pois ainda estão sendo referenciados. Se esse objeto contiver um DbContext ou outros serviços nele, isso poderá fazer com que o uso de memória do aplicativo cresça ao longo do tempo. Esse comportamento geralmente é um sinal de um vazamento de memória. O EF Core lança uma exceção sempre que se depara com constantes de um tipo que não podem ser mapeadas usando o provedor de banco de dados atual. As causas comuns e suas soluções são as seguintes:
- Usando um método de instância: Ao usar métodos de instância em uma projeção de cliente, a árvore de expressão contém uma constante da instância. Se o seu método não usar nenhum dado da instância, considere tornar o método estático. Se você precisar de dados de instância no corpo do método, passe os dados específicos como um argumento para o método.
-
Passando argumentos constantes para o método: Este caso surge geralmente usando
this
em um argumento para o método cliente. Considere dividir o argumento em vários argumentos escalares, que podem ser mapeados pelo provedor de banco de dados. - Outras constantes: Se uma constante é encontrada em qualquer outro caso, então você pode avaliar se a constante é necessária no processamento. Se for necessário ter a constante, ou se você não puder usar uma solução dos casos acima, crie uma variável local para armazenar o valor e use a variável local na consulta. O EF Core converterá a variável local em um parâmetro.
Versões anteriores
A seção a seguir se aplica às versões do EF Core anteriores à 3.0.
As versões mais antigas do EF Core suportavam a avaliação do cliente em qualquer parte da consulta, não apenas na projeção de nível superior. É por isso que consultas semelhantes a uma postada na seção Avaliação de cliente sem suporte funcionaram corretamente. Como esse comportamento pode causar problemas de desempenho despercebidos, o EF Core registrou um aviso de avaliação do cliente. Para obter mais informações sobre como exibir a saída do log, consulte Logging.
Opcionalmente, o EF Core permitia que você alterasse o comportamento padrão para lançar uma exceção ou não fazer nada ao fazer a avaliação do cliente (exceto na projeção). O comportamento de lançamento de exceções o tornaria semelhante ao comportamento na versão 3.0. Para alterar o comportamento, você precisa configurar avisos ao configurar as opções para seu contexto - normalmente no DbContext.OnConfiguring
, ou no Startup.cs
se estiver usando o ASP.NET Core.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}