Skip to main content

Performance no EFCore | C#

  • Performance no EFCore | C#

🚀 Como melhorar performance no EF Core 8

1. Use AsNoTracking() sempre que possível

Para consultas somente leitura, utilize AsNoTracking() para desativar o change tracking — reduzindo uso de CPU e memória:

var authors = await context.Authors
.AsNoTracking()
.Where(a => a.IsActive)
.ToListAsync();

Isso evita o overhead de rastrear entidades que não serão modificadas ([LinkedIn][1], [LinkedIn][2]).


2. Use consultas compiladas (Compiled Queries)

Consultas LINQ são compiladas em SQL, e podem gerar overhead se forem executadas muitas vezes. Consultas compiladas evitam recompilação, acelerando cenários de alta frequência:

static readonly Func<MyDbContext,int,IAsyncEnumerable<Blog>>
_compiledBlogs = EF.CompileAsyncQuery(
(MyDbContext ctx, int minPosts) =>
ctx.Blogs.Where(b => b.PostCount > minPosts));

await foreach (var blog in _compiledBlogs(context, 10))
{
Console.WriteLine(blog.Title);
}

Esse tipo de query pode reduzir tempo de execução de ~670 μs para ~560 μs em benchmarks ([LinkedIn][1]).


3. Indexação adequada no banco

Crie índices nos campos mais usados em filtros, ordenações e junções. Você pode configurar direto no modelo EF:

modelBuilder.Entity<Order>()
.HasIndex(o => o.CustomerId);

Índices diminuem drasticamente os tempos de busca no banco.


4. Paginação e projeções (Select)

Evite carregar grandes volumes de dados em memória: use Select() para trazer apenas campos necessários e Skip()/Take() para paginação:

var page = await context.Orders
.Where(o => o.Status == status)
.Select(o => new { o.Id, o.Total, o.Date })
.Skip((pageNum - 1) * pageSize)
.Take(pageSize)
.ToListAsync();

Isso reduz consumo de rede, memória e tempo de resposta.


5. Eager vs Lazy Loading e Detectando explosão cartesiana

  • Prefira eager loading quando for carregar entidades relacionadas para evitar múltiplas consultas (N+1).
  • Use explicit loading ou Include().AsSplitQuery() em cenários complexos, evitando explosões de duplicação de dados:
var books = context.Authors
.Include(a => a.Books)
.AsSplitQuery()
.ToList();

Isso evita duplicações e melhora desempenho em relacionamentos 1:N.

Untitled


🎯 Novidades do EF Core 8 que ajudam na performance

✅ Complex Types

EF Core 8 agora suporta Complex Types, que permitem mapear objetos compostos sem gerar uma tabela separada. Isso simplifica o modelo e melhora performance em inserções e consultas:

public class Customer {
public int Id { get; set; }
public Address Address { get; set; }
}
public class Address {
public string City { get; set; }
public string Country { get; set; }
}

No banco, os campos de Address virão na mesma tabela do Customer ([Dan Does Code][4]).

✅ Bulk Updates & Deletes aprimorados

Use ExecuteUpdate() e ExecuteDelete() para realizar operações em lote no banco sem carregar entidades em memória:

await context.Orders
.Where(o => o.Status == Status.Pending)
.ExecuteUpdateAsync(b => b.SetProperty(o => o.Status, Status.Processed));

Reduz round-trips e melhora eficiência em operações de grande escala ([InfoWorld][5], [LinkedIn][2]).


🧪 Outras boas práticas adicionais para EF Core 8

  1. Use DbContextPool para reduzir overhead de criação de contexto

    services.AddDbContextPool<MyDbContext>(opt => opt.UseSqlServer(conn));

    Isso reutiliza objetos e economiza memória e CPU ([Microsoft Learn][6]).

  2. Desative AutoDetectChangesEnabled durante bulk operations

    context.ChangeTracker.AutoDetectChangesEnabled = false;
    context.UpdateRange(entities);
    await context.SaveChangesAsync();
    context.ChangeTracker.AutoDetectChangesEnabled = true;

    Remove detecção automática de mudanças, acelerando operações em massa ([LinkedIn][2]).

  3. Evite criar queries com Expression API incorreta Evite usar Expression com valores constantes que causem recompilação da consulta; prefira parâmetros simples:

    var title = "post1";
    var post = await context.Posts.FirstOrDefaultAsync(p => p.Title == title);

    Isso permite reutilização do cache de query.

  4. Use async em vez de sincrono

    await context.Entities.ToListAsync();
    await context.SaveChangesAsync();

    Melhora escalabilidade e responsividade da aplicação ([InfoWorld][5], [LinkedIn][1]).

  5. Monitore consultas e gargalos Use logging do EF Core e ferramentas como Application Insights para identificar consultas lentas e otimizar o plano de execução do banco de dados ([LinkedIn][1]).


📚 Exemplos consolidados: Comparativo

RecursoPraktica recomendadaBenefício
AsNoTrackingcontext.Entities.AsNoTracking()Menor uso de memória e CPU em leitura
Consultas compiladasEF.CompileAsyncQuery()Evita recompilação LINQ
IndexaçãoHasIndex() no modeloBusca mais rápida no BD
Paginação & projeções.Skip().Take() + Select()Menor payload transferido
Loading eficienteInclude()/AsSplitQuery()Evita múltiplos queries e duplicação
Bulk updatesExecuteUpdate() / ExecuteDelete()Eficiência sem carregar entidades
Pool de DbContextAddDbContextPool()Reúso de instâncias e menos GC overhead
Desativar detecção automáticaAutoDetectChangesEnabled = falseMelhor performance em lotes
AsyncToListAsync(), SaveChangesAsync()Melhor escalabilidade e performance I/O
MonitoramentoLogs e APM (por ex. App Insights)Facilita diagnóstico e otimização contínua