7 Erros Fatais no EF Core: Dicas Para Otimizar Consultas

Imagine que você tenha construído uma aplicação inteirinha com Entity Framework Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real. e, quando coloca em produção, percebe que as consultas estão lerdas, travando o sistema e deixando usuários irritados. Isso pode acontecer por vários motivos, mas existem 7 erros clássicos (e mortais!) que todo mundo comete pelo menos uma vez na vida. Vamos explorar cada um, entender o porquê deles acontecerem e, claro, como corrigir e otimizar seu código para ganhar performance.

Índice🔗

Pecado #1: Carregar o Universo com Include Exagerado🔗

O uso abusivo do Include é um clássico. Às vezes, você quer trazer dados relacionados, mas acaba incluindo tabelas e tabelas sem nem precisar. Isso gera uma consulta🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. enorme e um “carregamento do universo”.

Exemplo de como não fazer:

var pedidos = context.Pedidos
    .Include(p => p.Cliente)
    .Include(p => p.Cliente.Endereco)
    .Include(p => p.Itens)
    .ThenInclude(i => i.Produto)
    .Include(p => p.Status)
    .ToList();

Se você só vai exibir na tela o nome do cliente e a data do pedido, não faz sentido trazer endereço, itens e produto🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!Aprenda a dominar operadores aritméticos em C# com exemplos práticos, técnicas de cálculo e dicas para evitar erros e maximizar resultados.. Cada Include extra representa mais carga ao banco e📊 Behavior-Driven Development: Testes que Todo Mundo Entende!📊 Behavior-Driven Development: Testes que Todo Mundo Entende!Descubra como o BDD transforma testes em linguagens acessíveis. Aprenda a usar SpecFlow em C# para criar testes claros, colaborativos e sem ambiguidades. ao transporte de dados.

Como Resolver:

Pecado #2: Usar Lazy Loading sem Limites (N+1 Problem)🔗

O Lazy Loading pode ser conveniente, mas é traiçoeiro: se você tem uma lista de 100 itens, e para cada item você acessa uma propriedade⚡ Propriedades: Get e Set com Elegância (e sem Campos Privados Bagunçados)!⚡ Propriedades: Get e Set com Elegância (e sem Campos Privados Bagunçados)!Aprenda como utilizar propriedades em C# para encapsular dados, validar informações e manter um código organizado, seguro e de fácil manutenção. de navegação, o EF Core pode disparar 100 consultas extras (uma para cada item!), culminando no famoso “N+1 Problem”.

Exemplo de código problemático:

var pedidos = context.Pedidos.ToList();
foreach (var pedido in pedidos)
{
    Console.WriteLine(pedido.Cliente.Nome);
    // Aqui pode ocorrer uma query para cada pedido, se Lazy Loading estiver habilitado
}

Como Resolver:

Pecado #3: Falta de Paginação e Filtros Claros🔗

Trazer milhares de registros de uma só vez pode explodir sua memória e demorar um tempão. Paginar resultados e filtrar🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. com precisão ajuda muito o banco e também a aplicação a lidar melhor com os dados.

Exemplo de paginação simples:

int pagina = 1;
int tamanhoPagina = 10;
var clientesPaginados = context.Clientes
    .OrderBy(c => c.Nome)
    .Skip((pagina - 1) * tamanhoPagina)
    .Take(tamanhoPagina)
    .ToList();

Com paginação, você economiza recursos do banco e do transporte de dados, além de melhorar a experiência🌐 LinkedIn para Devs .NET: Perfil que Atrai Recrutadores!🌐 LinkedIn para Devs .NET: Perfil que Atrai Recrutadores!Aprenda a otimizar seu perfil LinkedIn com dicas essenciais para devs .NET. Conquiste oportunidades e destaque suas habilidades! do usuário.

Pecado #4: Não Usar AsNoTracking quando Necessário🔗

Para buscas meramente informativas (quando não há atualização de dados), o EF Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real. não precisa manter o “tracking” de tudo em memória. Usar AsNoTracking() impede que cada objeto seja monitorado para alterações, melhorando bastante a performance🔄 StringBuilder: Quando Concatenar Strings Vira um Pesadelo!🔄 StringBuilder: Quando Concatenar Strings Vira um Pesadelo!Descubra como o StringBuilder otimiza a concatenação em C#, evitando desperdício de memória e melhorando a performance das aplicações. Veja exemplos práticos! em cenários de leitura pesada.

var produtos = context.Produtos
    .AsNoTracking()
    .Where(p => p.Ativo)
    .ToList();

Se a ideia é só exibir dados, não tem por que manter tracking das entidades.

Pecado #5: Ignorar a Seleção de Campos Específicos (Projeção)🔗

Muitas vezes, você só quer duas ou três colunas, mas busca a entidade inteira, com dezenas de propriedades⚡ Propriedades: Get e Set com Elegância (e sem Campos Privados Bagunçados)!⚡ Propriedades: Get e Set com Elegância (e sem Campos Privados Bagunçados)!Aprenda como utilizar propriedades em C# para encapsular dados, validar informações e manter um código organizado, seguro e de fácil manutenção.. O EF Core tem suporte a projeção usando Select🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio., que evita trazer dados desnecessários.

var listaNomesPrecos = context.Produtos
    .Select(p => new { p.Nome, p.Preco })
    .AsNoTracking()
    .ToList();

Além de reduzir o volume de dados transferidos, isso pode aliviar o tempo de retorno do banco enormemente.

Pecado #6: Falta de Índices e Ajustes no Banco🔗

Mesmo com EF Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real., seu banco ainda precisa de uma boa modelagem e índices adequados. Se você não tiver índices nas colunas que são frequentemente usadas em Where🔍 LINQ Básico: Filtre Dados como um Garimpeiro Digital!🔍 LINQ Básico: Filtre Dados como um Garimpeiro Digital!Descubra como o LINQ facilita o processamento de dados em C#. Filtre, ordene e transforme coleções com precisão e eficiência no seu código. ou Join, sua consulta🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. vai ser superlenta.

Dicas🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!Aprenda a dominar operadores aritméticos em C# com exemplos práticos, técnicas de cálculo e dicas para evitar erros e maximizar resultados. para não pecar aqui:

Pecado #7: Consultas Síncronas Gigantes (Esquecendo o Async)🔗

Em aplicações modernas, usar operações assíncronas ajuda a liberar threads enquanto🔄 Loops em C#: Repita Tarefas sem Enlouquecer (Com for e while!)🔄 Loops em C#: Repita Tarefas sem Enlouquecer (Com for e while!)Descubra como automatizar repetições em C# utilizando loops for e while com exemplos práticos que evitam erros e otimizam seu código. Aprenda mais! espera a resposta do banco. Se você chamar .ToList() para grandes consultas🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. e não usar .ToListAsync(), pode travar a thread de requisição inteira, resultando em lentidão geral.

var listaProdutos = await context.Produtos
    .Where(p => p.Estoque > 0)
    .ToListAsync();

Com isso, seu servidor lida melhor com múltiplas requisições ao mesmo tempo e📊 Behavior-Driven Development: Testes que Todo Mundo Entende!📊 Behavior-Driven Development: Testes que Todo Mundo Entende!Descubra como o BDD transforma testes em linguagens acessíveis. Aprenda a usar SpecFlow em C# para criar testes claros, colaborativos e sem ambiguidades. evita gargalos desnecessários.

Tracking Desnecessário: O Assassino Silencioso🔗

O Crime:

var produtos = context.Produtos.ToList(); // Tracking automático!

O EF Core rastreia todas as entidades por padrão, consumindo memória e CPU mesmo em consultas🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. de apenas leitura.

A Redenção:

var produtos = context.Produtos
    .AsNoTracking()
    .ToList(); // Desliga o rastreamento

Impacto: Redução de até 40% no consumo de memória🔄 StringBuilder: Quando Concatenar Strings Vira um Pesadelo!🔄 StringBuilder: Quando Concatenar Strings Vira um Pesadelo!Descubra como o StringBuilder otimiza a concatenação em C#, evitando desperdício de memória e melhorando a performance das aplicações. Veja exemplos práticos! em consultas grandes (Fonte: Microsoft Benchmarks⏱️ Benchmark.NET: Meça a Velocidade do Seu Código!⏱️ Benchmark.NET: Meça a Velocidade do Seu Código!Aprenda a usar o Benchmark.NET para medir a performance de seu código com precisão. Descubra dicas, práticas confiáveis e evite erros comuns..

Select N+1: O Pesadelo das Queries Invisíveis🔗

O Crime:

var pedidos = context.Pedidos.ToList();
foreach (var pedido in pedidos)
{
    // Gera uma nova query para cada pedido!
    var cliente = context.Clientes.Find(pedido.ClienteId);
}

A Redenção:

var pedidos = context.Pedidos
    .Include(p => p.Cliente) // Carrega tudo de uma vez
    .ToList();

Dica: Use ThenInclude para🔄 Loops em C#: Repita Tarefas sem Enlouquecer (Com for e while!)🔄 Loops em C#: Repita Tarefas sem Enlouquecer (Com for e while!)Descubra como automatizar repetições em C# utilizando loops for e while com exemplos práticos que evitam erros e otimizam seu código. Aprenda mais! hierarquias complexas:

.Include(p => p.Itens)
    .ThenInclude(i => i.Produto)

Consultas em Loop: Quando o Laço Vira uma Corda🔗

O Crime:

foreach (var id in idsDeClientes)
{
    var cliente = context.Clientes.Find(id); // 1000 ids = 1000 queries!
}

A Redenção:

var clientes = context.Clientes
    .Where(c => idsDeClientes.Contains(c.Id))
    .ToList(); // 1 query para todos

Impacto: Consulta🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. com 1000 IDs: 100x mais rápido ⚡

Carregar Tudo: O Hambúrguer de 10 Andares🔗

O Crime:

var clientes = context.Clientes.ToList(); // SELECT * FROM Clientes

A Redenção:

var clientes = context.Clientes
    .Select(c => new
    {
        c.Id,
        c.Nome,
        c.Email
    }).ToList(); // SELECT apenas colunas necessárias

Bônus: Use DTOs para consultas🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. complexas:

public class ClienteResumoDto
{
    public int Id { get; set; }
    public string Nome { get; set; }
}
var clientes = context.Clientes
    .Select(c => new ClienteResumoDto { /* ... */ })
    .ToList();

Índices Fantasmas: A Ilusão da Performance🔗

Sintomas:

Solução Mágica:

CREATE INDEX IX_Clientes_Email ON Clientes (Email);

No EF Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real.:

modelBuilder.Entity<Cliente>()
    .HasIndex(c => c.Email)
    .IsUnique();

Regra de Ouro: Índices em colunas usadas em filtros, ordenações🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio. e joins.

Processamento em Memória: O Disfarce de Query🔗

O Crime:

var produtos = context.Produtos
    .ToList() // ⚠️ Traz TUDO para a memória!
    .Where(p => p.Preco > 1000);

A Redenção:

var produtos = context.Produtos
    .Where(p => p.Preco > 1000) // Filtro no SQL
    .ToList();

Comparação:

CenárioRegistros no BDTempo
ToList + Where1.000.00015s ⚠️
Where + ToList1.000.0002s ✅

Paginação Ingênua: O Falso Amigo🔗

O Crime:

var page1 = context.Produtos.Skip(0).Take(10).ToList();
var page2 = context.Produtos.Skip(10).Take(10).ToList();
// ...

Problema🤝 GitHub Básico: Versionamento para Iniciantes!🤝 GitHub Básico: Versionamento para Iniciantes!Descubra como o GitHub facilita colaboração, versionamento e organização de código com este tutorial prático e essencial para desenvolvedores iniciantes.: Skip fica mais lento conforme a página aumenta.

Solução Profissional:

var ultimoId = page1.Last().Id;
var page2 = context.Produtos
    .Where(p => p.Id > ultimoId)
    .Take(10)
    .ToList();

Vantagem: Tempo constante ⏱️ independente da página!

Bônus: Diagnóstico de Performance

1. Log de Consultas🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!🎲 Desafio: Analise Dados de Vendas com LINQ e Coleções!Aprenda a usar coleções e LINQ em C# para analisar vendas, filtrar dados e extrair insights estratégicos que otimizem decisões e impulsionem seu negócio.:

optionsBuilder.UseSqlServer(connectionString)
    .LogTo(Console.WriteLine, LogLevel.Information);

2. Analise o Plano de Execução:

EXEC sp_executesql N'SELECT * FROM Clientes WHERE Id = @p0', N'@p0 int', 123
-- Clique em "Execution Plan" no SSMS

3. EF Core🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!🌍 Projeto: API de E-Commerce com ASP.NET Core e SQL Server!Aprenda a construir uma API robusta para e-commerce com ASP.NET Core, EF Core, JWT e Swagger, validando suas habilidades em um projeto prático real. Insights:

services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString)
        .EnableSensitiveDataLogging()
        .EnableDetailedErrors());

Lembre-se: Monitorar é tão importante quanto otimizar! 🕵️♂️

No final das contas, a performance do EF Core não é uma questão mágica, mas uma soma de boas práticas🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!🔢 Operadores Aritméticos: Faça Cálculos como uma Calculadora Humana!Aprenda a dominar operadores aritméticos em C# com exemplos práticos, técnicas de cálculo e dicas para evitar erros e maximizar resultados. de código, design de banco e monitoramento constante. Evitar esses 7 pecados já vai te colocar em um patamar bem mais tranquilo e performático!

Autor: Marcelo V. Souza - Engenheiro de Sistemas e Entusiasta em IoT e Desenvolvimento de Software, com foco em inovação tecnológica.

Referências🔗

Compartilhar artigo

Artigos Relacionados