performance C# EF

API ASP.NET Core con EF Core e SQL Server errori da evitare

Quando si implementano delle API con .net Core utilizzando Entity Framework, alcuni problemi di prestazioni non dipendono dalla logica ma come si utilizzano le chiamate ad Entity Framework. Di seguito alcune regole base da rispettare per evitare problemi di prestazioni.

1. Recupera solo i dati necessari (proiezioni)

❌ Errato:

var users = await _dbContext.Users.ToListAsync();

— recuperi TUTTE le colonne (nome, email, password hash, immagine profilo…).

✅ Corretto:

var users = await _dbContext.Users
    .Select(u => new { u.Name, u.Email })
    .ToListAsync();

Proiettando solo ciò che serve riduci carico, velocizzi serializzazione e ottimizzi performance. Anche Microsoft lo consiglia nelle linee guida su querying efficiente e proiezioni (Microsoft Learn).

2. Evita il problema N+1 con Include

❌ Errato:

var users = await _dbContext.Users.ToListAsync();
foreach(var u in users)
    var orders = await _dbContext.Orders.Where(o => o.UserId == u.Id).ToListAsync();

— N+1 round‑trip al DB.

✅ Soluzione:

var users = await _dbContext.Users
    .Include(u => u.Orders)
    .ToListAsync();

Include obbliga EF a caricare relazioni in un’unica query. Usa sempre strumenti di profiling (SQL Profiler, logging EF) per identificare problemi (Microsoft Learn, Microsoft Learn).

3. Utilizza AsNoTracking() per letture in sola lettura

Per query che non modificano entità:

var users = await _dbContext.Users
    .AsNoTracking()
    .ToListAsync();

Riduce l’overhead di change tracking, abbassando RAM e migliorando performance su grandi dataset. Microsoft consiglia l’uso in query read-only (Medium).

⚠️ Applica AsNoTracking() una sola volta all’inizio della query; non è necessario ripeterlo per ogni Include (Stack Overflow).

4. SQL raw parametrici: evita SQL injection

❌ Errato:

.FromSqlRaw($"SELECT * FROM Users WHERE Name = '{inputName}'")

✅ Corretto:

.FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {inputName}")

Oppure usa LINQ puro. Le query interpolated garantiscono parametrizzazione sicura (Microsoft Learn).

5. Index mancanti = query lente

Assicurati che le colonne usate nei WHERE o join siano indicizzate:

CREATE NONCLUSTERED INDEX IX_Orders_UserId ON Orders(UserId);

Verifica il piano di esecuzione per trovare colonne non indicizzate e filtrare correttamente (Microsoft Learn).

6. Usa sempre metodi async in metodi async

❌ Errato:

var data = _dbContext.Users.ToList(); // Sincrono!

✅ Corretto:

var data = await _dbContext.Users.ToListAsync();

Mischiare sync e async può bloccare thread e costringere il thread pool in situazioni di carico elevato (Microsoft Learn).

7. Non tenere DbContext in vita troppo a lungo

Il DbContext è pensato per essere breve (per richiesta web). Registraralo con AddDbContext (scoped), evita di conservarlo in servizi singleton — altrimenti rischi memory leak o bug di concorrenza (Microsoft Learn, Microsoft Learn).

8. Proiezioni manuali invece di AutoMapper in proiezioni grandi

AutoMapper è utile, ma può introdurre inefficienze se non controlli i campi caricati: meglio usare .Select(...) manuale quando vuoi proiettare molti campi o avere controllo sui dati.

9. Usa transazioni con criterio

EF Core avvolge SaveChanges() in una transazione per default. Non ne aggiungere all’occhio se non necessarie.
✅ Solo quando servono più operazioni atomiche:

using var txn = await context.Database.BeginTransactionAsync();
...
await txn.CommitAsync();

Evita transazioni annidate non necessarie.

10. Query tagging nelle produzioni

Usa .TagWith("MiTagQuery") per marcare la query generata e facilitarne il debug nei log di SQL. Ottimo approccio per profilazione in produzione.


Qualche consiglio avanzato ulteriore dalla documentazione ufficiale

  • Compiled queries: puoi pre‑compilare query frequenti con EF.CompileAsyncQuery(...) per ridurre il tempo di “compilazione” LINQ-to-SQL e migliorare latenza (Microsoft Learn).
  • DbContext pooling: attivando il pooling (AddDbContextPool) riduci overhead di creazione e distruzione dei contesti, utile se hai molte richieste contemporanee (Microsoft Learn).
  • Compiled models: su modelli EF Core grandi, puoi generare un modello compilato con dotnet ef dbcontext optimize per diminuire i tempi di startup (Microsoft Learn).

📋 Riepilogo tabellare

Problema comuneSoluzione consigliata
Recuperi troppi campiUsa .Select(...) con proiezione
Caricamenti relazioni inefficientiUsa .Include(...)
Tracking non necessarioUsa .AsNoTracking()
Parametri SQL vulnerabiliUsa .FromSqlInterpolated(...)
Query su colonna non indicizzataAggiungi index e analizza piano di esecuzione
Mix sync/asyncUtilizza sempre .ToListAsync()
DbContext è troppo duraturoRegistralo scoped, evita singleton
AutoMapper su proiezioni complesseMeglio mappatura manuale con .Select(...)
Transazioni non necessarieUsa solo se serve atomicità multi-operazione
Debug produzioneTagga query con .TagWith(...).

Seguire queste linee guida può fare la differenza tra un’API lenta e una scalabile, mantenibile e veloce. Le fonti ufficiali Microsoft (Entity Framework Core – performance guide, efficient querying, compilazione, tracking) sono una base concreta su cui costruire API efficienti e affidabili (Microsoft Learn).


Pubblicato

in

,

da