Andiamo dritti al punto: cos'è davvero EF Core?
Se stai scrivendo codice in .NET, prima o poi ti scontrerai con la necessità di parlare con un database. Puoi farlo scrivendo query SQL a mano, ma ammettiamolo: è noioso, ripetitivo e prono a errori banali che ti fanno perdere ore di debug.
Qui entra in gioco Entity Framework (EF) Core. Non è solo un tool, è un ORM (Object-Relational Mapper). In parole povere? Traduce le tue classi C# in tabelle di un database e viceversa.
Proprio così. Niente più mapping manuali infiniti o stringhe SQL sparse per tutto il progetto.
Ma attenzione. Molti sviluppatori cadono nella trappola di pensare che EF Core sia una "scatola magica". Chi lo usa senza capire cosa succede sotto il cofano finisce per scrivere codice lentissimo, con query che caricano mezzo database solo per leggere un nome utente. Un classico.
Code First vs Database First: quale strada scegliere?
Questa è la domanda che divide i team di sviluppo sin dal primo giorno di progetto. Non c'è una risposta univoca, ma c'è una scelta razionale basata sul contesto.
L'approccio Code First è il preferito da chi ama avere il controllo totale nel codice. Definisci le tue entità in C#, configuri le relazioni tramite la Fluent API e lasci che EF Core generi le migrazioni per aggiornare il database. È fluido, veloce e perfetto per i nuovi progetti.
Un dettaglio non da poco: le migrazioni sono l'anima di questo approccio. Ti permettono di versionare lo schema del database esattamente come fai con il codice su Git.
Poi c'è il Database First. Immagina di entrare in un progetto legacy dove il database esiste già da dieci anni, è enorme e gestito da un DBA che non vuole che tu tocchi una singola colonna. In questo caso, fai l'operazione inversa: leggi lo schema esistente e generi le classi C# automaticamente.
Quale scegliere? Se il database è il cuore pulsante e preesistente, vai di Database First. Se stai costruendo qualcosa da zero, Code First non ha rivali.
Le performance: dove si nascondono i problemi
Scrivere _context.Users.ToList() sembra semplice. Finché non hai un milione di record. A quel punto l'applicazione crasha o diventa lenta come una tartaruga.
Il primo errore da evitare è il caricamento avidità, ovvero l'Eager Loading indiscriminato. Usare .Include() su ogni tabella correlata crea join mostruosi che uccidono le performance del server SQL.
A volte è meglio usare il Lazy Loading, ma occhio al problema delle N+1 query. Ti ritrovi a fare una chiamata al database per l'entità principale e poi altre cento chiamate in un ciclo for per recuperare i dettagli. Un suicidio prestazionale.
La soluzione? Impara a usare le proiezioni.
Invece di scaricare l'intero oggetto, seleziona solo quello che ti serve:
.Select(u => new { u.Name, u.Email })
Semplice. Pulito. Efficiente. Stai chiedendo al database solo due colonne invece di venti. Il risparmio in termini di memoria e tempo di risposta è immediato.
La Fluent API: quando gli attributi non bastano
Molti iniziano usando i Data Annotations, quei piccoli attributi come [Required] o [MaxLength(100)] sopra le proprietà della classe. Funzionano bene per le cose semplici.
Ma appena le relazioni si fanno complesse, gli attributi diventano insufficienti e sporcano il codice del dominio.
La Fluent API, configurata all'interno del metodo OnModelCreating, è dove avviene la vera magia. Qui puoi definire indici univoci, vincoli di check complessi e configurazioni precise per le chiavi esterne senza inquinare le tue classi POCO.
È un modo molto più professionale di gestire lo schema. Separi cosa è l'entità da come viene salvata nel database.
Tracking vs No-Tracking: il trucco per leggere velocemente
Per impostazione predefinita, EF Core tiene traccia di ogni entità che recupera dal database. Questo serve per sapere se l'oggetto è stato modificato e aggiornarlo con un semplice SaveChanges().
Ma se devi solo mostrare dei dati in una tabella HTML o in un'API di sola lettura, il tracking è inutile. È puro spreco di CPU e RAM.
Usa AsNoTracking().
Aggiungendo questo metodo alle tue query di sola lettura, dici a EF Core: "Prendi i dati e dimenticatene subito". La velocità di esecuzione aumenta sensibilmente, specialmente su set di datiConsistentemente grandi.
Gestire le transazioni senza impazzire
Il metodo SaveChanges() avvolge automaticamente le operazioni in una transazione. Se aggiorni tre tabelle e la terza fallisce, EF Core annulla tutto. Sicurezza massima.
Però, cosa succede se devi coordinare più contesti diversi o integrare logiche esterne al database? Allora devi gestire la transazione manualmente con BeginTransaction().
Non è un'operazione comune per ogni singola chiamata, ma in processi critici come i pagamenti o l'aggiornamento di inventari, è l'unica strada percorribile per garantire l'integrità dei dati.
Consigli finali per chi vuole scalare
Lavorare con entity framework .net richiede un equilibrio tra comodità e controllo. Il rischio è affidarsi troppo all'astrazione e dimenticare che, alla fine, c'è sempre un motore SQL che deve eseguire del lavoro.
Controlla sempre i log delle query generate. Usa strumenti come SQL Server Profiler o semplicemente guarda la console di debug durante lo sviluppo. Se vedi una query lunga tre pagine per un semplice filtro, fermati e riscrivila.
Non aver paura di usare il Raw SQL quando EF Core diventa troppo macchinoso. Metodi come FromSqlInterpolated ti permettono di scrivere SQL puro mantenendo i vantaggi del mapping degli oggetti. È il compromesso ideale per le query analitiche complesse.
In fondo, l'obiettivo non è usare EF Core perché "si deve fare", ma usarlo per accelerare lo sviluppo senza sacrificare la stabilità del sistema.