La guida definitiva e più completa su Entity Framework per accedere ai dati
Entity Framework,
il pensiero da architetto
Questa non è la solita guida a Entity Framework, quella fatta di metodi da imparare a memoria. È il modo di ragionare di chi progetta il punto esatto in cui un programma parla con i suoi dati. E qui quel punto non te lo descrivo, te lo faccio vedere mentre accade. Poche righe più in basso trovi già una dimostrazione dal vivo del problema N+1: una schermata che sul portatile di uno sviluppatore vola, e che davanti al tuo cliente si trascina, con il conto delle query che parte sotto i tuoi occhi.
È costruita come un percorso, non come un manuale da consultare. Una mappa iniziale ti fa scegliere da dove entrare; dieci capitoli portano dalle fondamenta (cos'è un ORM, il costo nascosto delle letture, il SQL che viene davvero generato) fino alle decisioni difficili: la concorrenza che fa sparire una scrittura senza un errore, il DbContext che cresce fuori controllo, le migrazioni in cui i dati si perdono per sempre, e i casi in cui EF non è la risposta. Otto capitoli finali affrontano ciò che nel 2010 non esisteva ancora: cosa succede quando è un'AI a scrivere il tuo strato dati, e come metterle un cancello prima che lo faccia lei.
Il valore non è nelle nozioni, quelle le trovi ovunque. È nelle decisioni: quelle che non compaiono in nessuna schermata, vivono in silenzio nel codice e presentano il conto anni dopo, quando cambiarle costa un rifacimento invece di una riga. Qui dentro ci sono ventisei anni passati esattamente in quel punto, distillati in criteri che puoi difendere davanti a un cliente, a un revisore o a un fascicolo tecnico.
Leggila adesso, non quando il problema è già in produzione: è pensata per essere studiata prima, mentre le scelte costano ancora poco. Ogni capitolo si può toccare, con demo che giri tu, non screenshot, e si chiude con l'esame del senior: le domande per misurare quanto di tutto questo è diventato davvero tuo. Si comincia da qui.
Matteo Migliore
Il problema N+1, in diretta
Carichi una lista di 8 ordini e, per ognuno, ne mostri il cliente. Guarda quante query partono davvero verso il database.
Nota In sviluppo, con tre record di prova, non lo noti. In produzione, con i dati veri del cliente, la stessa schermata si trascina. Il codice non è cambiato: è cambiato solo il volume.
La mappa
Dai fondamenti dell'ORM all'architettura, fino a governare l'AI sul data layer. Salta dove ti serve.
Cos'è un ORM, perché è nato e quando serve davvero.
Prima di governarlo, conviene sapere che problema risolve e quali problemi non è fatto per risolvere.
Do per scontato che tu abbia già sentito nominare Entity Framework, e che il tuo team ci lavori tutti i giorni. Questo pezzo non serve a insegnartelo, serve a metterti sotto gli occhi la parola giusta prima che le cose si facciano scomode. Perché lo strumento di cui stiamo parlando è un traduttore, e ogni traduttore prende decisioni al posto tuo senza fermarsi a chiedertelo. I guasti più cari nei gestionali nascono quasi sempre da una decisione del genere, presa anni prima da qualcuno che non aveva idea di cosa stesse rinunciando.
Un ORM, sigla per Object-Relational Mapper, è uno strato che traduce automaticamente tra due mondi che non parlano la stessa lingua: gli oggetti del tuo codice (classi, proprietà, riferimenti in memoria) e le tabelle di un database relazionale (righe, colonne, chiavi). Nel mondo .NET l'ORM di riferimento è Entity Framework Core: tu scrivi query in C# con LINQ e ragioni per oggetti, e lui genera il SQL, apre la connessione, trasforma i risultati in istanze delle tue classi e tiene traccia delle modifiche da salvare.
È nato per risolvere un problema preciso, l'impedance mismatch: il codice a oggetti e il modello relazionale organizzano i dati in modi diversi, e cucire i due a mano è ripetitivo e pieno di errori. Prima degli ORM si scriveva a mano ogni comando, ogni lettura dei campi, ogni conversione di tipo: montagne di codice meccanico che non aggiungeva valore e si rompeva a ogni modifica dello schema. L'ORM cancella quel lavoro manuale e ti fa ragionare sul dominio invece che sull'idraulica.
A cosa serve, in concreto: ti dà produttività altissima sul lavoro comune con i dati, le letture e scritture di tutti i giorni, la gestione delle relazioni, le migrazioni dello schema versionate, il controllo della concorrenza. Su una gestionale, un e-commerce, un'API di linea di business, un'app desktop WPF con database, EF Core copre la stragrande maggioranza di ciò che serve, e lo fa bene.
Ma un ORM non è l'unico strumento, e non è sempre quello giusto. Le alternative principali, in .NET, sono: i micro-ORM come Dapper, che mappano i risultati sugli oggetti ma ti lasciano scrivere il SQL a mano, ottimi quando vuoi controllo totale sulla query con pochissimo overhead; l'accesso grezzo con ADO.NET e SqlClient, il livello più basso e più veloce, dove gestisci tu ogni dettaglio; le stored procedure, quando la logica deve stare dentro il database; gli strumenti di bulk copy per inserire milioni di righe in un colpo. Fuori dal relazionale, se i dati non hanno forma tabellare, la scelta si sposta su database documentali o chiave-valore come Cosmos DB, MongoDB o Redis, dove un ORM relazionale non c'entra.
La regola da architetto è usare l'ORM come default e cambiare strumento dove i numeri lo chiedono. EF per il grosso, perché la produttività ripaga. Dapper o SQL diretto sui punti caldi, sulle query complesse, sui report pesanti. Bulk copy per i caricamenti massivi. Un altro database quando i dati non sono relazionali. Non è una scelta di fede tra ORM sì e ORM no: è scegliere lo strumento giusto per ogni tipo di problema, sapendo cosa ognuno costa.
Quando l'ORM è la scelta giusta
Gestionali, e-commerce, API di linea di business, app desktop con database relazionale. Ovunque conti la produttività sul lavoro dati di tutti i giorni e i volumi siano nella norma.
Quando conviene altro
Inserimenti massivi (bulk copy), letture ad altissimo volume o query molto complesse (Dapper, SQL diretto), footprint estremi su embedded, dati non relazionali (database documentali o chiave-valore).
| Strumento | Produttività | Controllo sul SQL | Performance grezza | Dove brilla |
|---|---|---|---|---|
| EF Core | Gestionali, API, CRUD, app di linea di business | |||
| Dapper (micro-ORM) | Query complesse, hot path, controllo totale sul SQL | |||
| ADO.NET / SqlClient | Massima velocità, ogni dettaglio in mano tua | |||
| Bulk copy | n/d | Inserire milioni di righe in un colpo | ||
| NoSQL (Cosmos, Mongo, Redis) | n/d | Dati non relazionali, chiave-valore, documenti |
Mettere un ORM in mezzo è una decisione, non una comodità.
EF non si impara metodo per metodo: si governa sapendo dove hai delegato il controllo.
Nessuno decide mai, in una riunione, di rinunciare al controllo sui propri dati. Lo si delega un pezzo alla volta, e ogni singolo pezzo sembra ragionevole nel momento esatto in cui lo deleghi. Il conto arriva più tardi, quando i dati veri sono diventati tanti e il tuo cliente ci sta già lavorando dentro. La domanda che viene subito dopo è la più banale del mestiere, e quasi nessuno se la pone finché non è troppo tardi.
Quando decidi di usare Entity Framework stai facendo una scelta di architettura, non una scelta di comodità. Stai mettendo uno strato di traduzione tra il modo in cui pensa il tuo codice, fatto di oggetti e relazioni in memoria, e il modo in cui pensa il database, fatto di righe, tabelle e insiemi. Quello strato ti regala una produttività enorme: scrivi in C# e qualcun altro scrive il SQL per te. Ma ogni traduzione automatica nasconde delle scelte, e le scelte che non vedi sono esattamente quelle che ti si ritorcono contro in produzione.
Un architetto lo sa e imposta il progetto di conseguenza. Non chiede al team di conoscere ogni metodo di EF a memoria: chiede al team di sapere, in ogni punto critico, cosa EF sta traducendo per loro e cosa quella traduzione costa. La differenza tra un progetto sano e uno malato dipende da quanto gli sviluppatori sono consapevoli di dove hanno delegato il controllo, più che dalla quantità di EF che conoscono.
Per gli sviluppatori
Il vostro lavoro non è evitare EF né dominarlo in ogni dettaglio. È sapere dove state delegando una decisione a lui, così quando qualcosa va storto sapete dove guardare invece di tirare a indovinare.
Per l'imprenditore
La produttività di EF è reale e ti fa risparmiare mesi. Ma quella stessa produttività, se il team non sa dove EF prende decisioni al posto suo, si trasforma in un debito che paghi tutto insieme quando il prodotto è già dai clienti.
La maggior parte del lavoro è leggere. Ed è lì che si spreca.
Le letture sono le operazioni più frequenti e le meno controllate.
Guarda la dimostrazione qui sotto prima di leggere una sola riga di spiegazione. Otto ordini sullo schermo, e verso il database partono nove richieste al posto di una. Sul portatile di chi scrive il codice, con tre righe di prova, non se ne accorge nessuno e il collaudo passa liscio. Con i dati veri del tuo cliente quella stessa schermata comincia a impiegare secondi, e nessuno sa spiegare perché, visto che il codice non è stato toccato. È cambiato soltanto il volume, ed è la prima cosa che vado a cercare quando entro in un'azienda.
Un'applicazione passa la vita a leggere: elenchi, dettagli, riepiloghi. Un architetto guarda una schermata e si chiede subito una cosa sola: per riempire questa vista, quante query partono davvero e quanti dati viaggiano che poi buttiamo via?
Il difetto più comune è quello che hai appena visto muoversi qui sopra: si carica una lista e, per ogni elemento, parte in automatico un'altra query. Dieci elementi diventano undici query. La seconda fonte di spreco è più subdola: caricare oggetti interi quando ne serve una fetta. Se una tabella mostra tre campi ma il codice si porta a casa l'entità intera con tutte le colonne e le relazioni, stai facendo lavorare database, rete e memoria per dati che non guarderai mai.
È un'abitudine mentale, prima ancora che una tecnica. È la differenza tra chi scrive codice che funziona sul suo portatile e chi scrive codice che regge quando ci sono mille utenti veri.
Per gli sviluppatori
Prima di dichiarare finita una schermata, guardate quante query genera e quanti dati porta a casa. Una vista che sul vostro PC vola può crollare in produzione, e il momento peggiore per scoprirlo è quando lo scopre il cliente.
Per l'imprenditore
Le lentezze che i clienti segnalano raramente nascono da hardware debole. Nascono da letture scritte senza controllo. È un problema di competenza, non di server: si risolve formando il team, non comprando macchine.
Guardare il SQL separa chi usa EF da chi lo governa.
Non scrivere SQL non significa non capire il SQL.
C'è un gesto che divide in due il mondo di chi lavora con i dati, e dura tre secondi. Guardare cosa il programma manda davvero al database. Uno sviluppatore con due o tre anni di esperienza non ha ancora le cicatrici per capire cosa sta guardando, e non è una colpa sua. Io quelle cicatrici me le sono fatte tutte, una alla volta, sul codice scritto da altri, e non ricordo una sola volta in cui questo gesto non abbia tirato fuori qualcosa.
C'è un gesto banale che divide i due mondi: leggere il SQL che EF genera davvero. L'esperto tiene sempre acceso, almeno sui punti critici, il log di ciò che parte verso il database. Perché è lì che si vede la verità: se una query usa gli indici o li ignora, se una join si è trasformata in un'esplosione di righe duplicate.
Dietro c'è un principio da architetto: non ti puoi fidare di ciò che non misuri. Un team che non ha mai visto il SQL prodotto dal proprio codice sta guidando con il parabrezza oscurato. Procede finché la strada è dritta, e va a sbattere alla prima curva in produzione.
SELECT * FROM Ordini o LEFT JOIN Clienti c ... LEFT JOIN Righe r ... -- 40 colonne, molte inutili -- righe duplicate dalla join
SELECT o.Id, o.Data, c.Nome FROM Ordini o JOIN Clienti c ... -- 3 colonne, zero sprechi
Per gli sviluppatori
Abituatevi a guardare il SQL generato sui punti caldi, non solo quando qualcosa va male. È l'unico modo per sapere se EF sta lavorando bene per voi o se state costruendo un problema che esploderà dopo.
Per l'imprenditore
Un team che sa leggere e ottimizzare il SQL vale come un'assicurazione. Sono le persone che ti evitano il degrado in produzione, la telefonata del cliente arrabbiato e la corsa notturna a tappare i buchi.
Il DbContext è un'unità di lavoro: nasce, lavora, muore.
Un errore che non si vede in una schermata, e si paga a interessi.
Ti racconto il tipo di guasto per cui mi chiamano. Non è quello che manda tutto in crash, perché quello si vede e si aggiusta in un pomeriggio, ma quello che non si riproduce mai due volte uguale, che sparisce quando lo guardi e ricompare mentre il cliente lavora. Dietro a quei fantasmi c'è quasi sempre un oggetto invisibile, che dovrebbe nascere e morire in fretta e invece resta vivo troppo a lungo. A te non serve il suo nome tecnico. Ti serve sapere che è la cosa che sta consumando le ore dei tuoi sviluppatori senza lasciare tracce da nessuna parte.
Va inteso come un'unità di lavoro: nasce quando inizia un'operazione, tiene traccia di ciò che accade, muore quando l'operazione finisce. Breve, focalizzato, usa e getta. Il problema nasce quando questa disciplina si perde: un context che vive troppo a lungo accumula in memoria tutto ciò che ha toccato, perché EF ne tiene una copia per capire cosa è cambiato.
Il risultato è un consumo di memoria che sale col tempo, bug di concorrenza difficili da riprodurre, comportamenti che sembrano magici solo perché nessuno ha in mente il ciclo di vita di quell'oggetto. Non c'è una schermata che ti dice che il context vive troppo. Lo scopri quando l'applicazione in produzione rallenta senza motivo apparente.
Per gli sviluppatori
Trattate il context come un oggetto usa e getta legato a una singola operazione. Se lo tenete vivo troppo o lo condividete, state preparando bug di memoria e concorrenza che vi tormenteranno senza lasciare tracce.
Per l'imprenditore
I bug che consumano più ore del team sono quelli che non si riproducono a comando. Molti nascono da questo oggetto invisibile gestito male. Sono ore fatturate a te, sotto forma di sviluppatori che cercano un fantasma.
Tracciare ciò che leggi soltanto è memoria pagata a vuoto.
Ogni volta che il programma legge un dato per mostrartelo, di norma se ne tiene anche una copia, per sapere se poi lo cambierai. Se stai soltanto guardando, quella copia è peso che paghi per niente. Su una schermata sola non te ne accorgi nemmeno, su migliaia di righe lette tutto il giorno diventa la differenza tra il server che hai e quello più grosso che qualcuno ti proporrà di comprare. Qui sotto la vedi crescere.
Ogni entità che EF traccia porta con sé una copia dei suoi valori originali, per poter dire cosa è cambiato al salvataggio. Su una lettura di sola visualizzazione quella copia non serve a niente: la stai pagando e basta. Disattivare il tracking dove leggi soltanto toglie lo snapshot e alleggerisce tutto.
La corruzione silenziosa dei dati non ti dà un errore.
Due utenti salvano lo stesso record. Senza precauzioni, uno sparisce.
C'è un modo di perdere dati che non ha bisogno di nessuno che sbagli. Il caso evidente è qualcuno che tocca un record che non doveva toccare, ma ne esiste una versione ancora più silenziosa. Due persone aprono la stessa scheda, la modificano, la salvano a pochi secondi di distanza, e una delle due modifiche svanisce senza che compaia un solo messaggio di errore. Premi il tasto qui sotto e guardala svanire.
Due persone aprono lo stesso record, lo modificano, lo salvano a pochi secondi l'una dall'altra. Senza nessuna precauzione la seconda scrittura sovrascrive la prima, e la modifica della prima persona sparisce nel nulla. Nessun errore, nessun avviso. Ci si accorge dopo, quando un dato sistemato risulta di nuovo sbagliato e nessuno sa spiegare perché.
insieme
Un architetto lo affronta prima che accada. Il meccanismo è collaudato: si aggiunge una sorta di sigillo che cambia a ogni modifica, e quando due scritture entrano in conflitto il sistema se ne accorge e solleva un'eccezione invece di lasciar vincere l'ultimo arrivato. A quel punto si decide con criterio: ricaricare, avvisare, tentare una fusione. L'importante è che la decisione sia esplicita, non lasciata al caso.
Per gli sviluppatori
La concorrenza non gestita non vi dà un errore, vi dà dati sbagliati che scoprirete settimane dopo. Decidete in anticipo cosa succede quando due scritture si scontrano, invece di lasciare che l'ultima cancelli l'altra in silenzio.
Per l'imprenditore
Un dato corrotto in silenzio è peggio di un crash. Un crash lo vedi e lo sistemi. Un dato sbagliato che nessuno nota mina la fiducia dei clienti nel prodotto, ed è un danno molto più difficile da riparare del codice.
Dove far entrare EF nel codice, e dove tenerlo lontano.
Spargere le chiamate al database ovunque è comodo all'inizio e ingestibile dopo.
Nel gestionale per studi legali che ho costruito, LegalDesk, la traccia di ogni modifica non è arrivata per fortuna. È arrivata perché qualcuno, anni prima, aveva deciso dove le cose dovevano stare e dove non dovevano entrare mai. Quel tipo di ordine non si costruisce il giorno in cui serve, e non si compra dopo. Il capitolo che segue è il più noioso da leggere di tutta questa pagina, ed è anche quello che ti presenta il conto più salato se lo salti.
Qui si vede il pensiero da architetto più che altrove. La domanda non è come usare EF, ma dove permettergli di entrare nel codice e dove no. Un errore diffuso è spargere le chiamate al database in ogni angolo dell'applicazione: nel controller che risponde alla richiesta, nel servizio che elabora, un po' ovunque. Sembra pratico, e all'inizio lo è. Ma produce un codice in cui logica di business e persistenza sono così intrecciate che non puoi toccare l'una senza rischiare di rompere l'altra, e non puoi testare niente senza un database vero acceso.
La disciplina opposta è tenere EF confinato in uno strato dedicato, con la logica di business che vive per conto suo, indipendente da come i dati vengono salvati. Il vantaggio non è estetico: puoi far evolvere le regole del business senza toccare la persistenza, puoi testare la logica in un istante senza un database, e quando un nuovo sviluppatore entra nel team capisce dove sta cosa.
EF è già, di suo, un gestore di transazioni e un accesso ordinato ai dati. L'architetto vero non ama le astrazioni per il gusto di averle: ne aggiunge una solo quando risolve un problema reale, e nel dubbio sceglie la semplicità. Saper distinguere l'astrazione che serve da quella che appesantisce è forse la competenza architetturale più preziosa e più rara.
Per gli sviluppatori
Tenete la logica di business separata dalla persistenza, ma non costruite cattedrali di astrazioni sopra EF solo perché si è sempre fatto così. Ogni strato che aggiungete deve giustificarsi con un problema concreto, altrimenti è peso morto.
Per l'imprenditore
Un'architettura con confini chiari è ciò che ti permette di aggiungere funzioni senza che ogni modifica ne rompa altre tre. È la differenza tra un software che accelera nel tempo e uno che rallenta fino a bloccarsi sotto il proprio peso.
Testare i dati: l'illusione della copertura facile.
Una finta memoria che imita il database ti dà sicurezza dove non c'è.
Ti hanno mai rassicurato dicendoti che i test sono tutti verdi? È una frase che vale la pena verificare, perché una barra verde può certificare il nulla. Esiste un modo comodo di far girare i test facendo finta di avere un database, e con quella finzione passano tranquillamente anche cose che in produzione si spaccano. Ho visto squadre bravissime dormire sonni sereni sopra una rete di sicurezza piena di buchi, senza che la colpa fosse di nessuno di loro. Qui sotto trovi, riga per riga, cosa un test controlla davvero e cosa non tocca nemmeno.
Testare il codice che tocca il database è più difficile di quanto sembri, e qui molti team si raccontano una bugia rassicurante. Esiste la possibilità di far girare i test contro una finta memoria che imita il database, veloce e comoda. Il problema è che quella finta memoria non si comporta come il database vero: non genera lo stesso SQL, non ha gli stessi indici, non rispetta gli stessi vincoli. Un test che passa contro la finzione ti dà un senso di sicurezza che il database reale può smentire alla prima occasione.
Un architetto costruisce una strategia a più livelli invece di affidarsi a un solo strumento. La logica pura, quella che non dipende dai dati, va tenuta separata proprio per poterla testare in un istante senza database. Ciò che invece riguarda le query, le performance, il SQL vero, va testato contro un database reale, magari usa e getta, perché è l'unico modo di verificare che le cose funzionino davvero come in produzione.
Per gli sviluppatori
La finta memoria per i test è comoda ma vi illude: non replica il database vero. Tenete i test veloci per la logica pura e usate un database reale dove contano SQL, indici e performance, altrimenti la vostra copertura è una rassicurazione falsa.
Per l'imprenditore
I test non sono un costo che rallenta lo sviluppo, sono ciò che permette al team di andare veloce senza rompere. Un progetto senza test affidabili è un progetto dove ogni nuova funzione mette a rischio quelle vecchie.
| Cosa stai controllando | Finta memoria | DB reale usa e getta |
|---|---|---|
| Logica di business pura | sì | sì |
| SQL realmente generato | no | sì |
| Indici e piano di esecuzione | no | sì |
| Vincoli e chiavi del database | no | sì |
| Concorrenza e transazioni | no | sì |
Le migrazioni e la produzione: dove un passo falso non si annulla.
In sviluppo cambiare lo schema è banale. Sui dati veri dei clienti non c'è il tasto annulla.
Questo è il capitolo in cui i dati si perdono per sempre. In sviluppo cambiare la struttura del database è un'operazione da due minuti che nessuno teme. Sui dati veri dei tuoi clienti non esiste il tasto annulla, e una colonna cancellata resta cancellata. Quando non c'è nessun posto dove andare a riprendere ciò che è stato perso, la distanza tra una telefonata disperata e una giornata normale sta tutta nella disciplina di chi preme invio.
Cambiare la struttura del database mentre l'applicazione è già in uso è uno dei momenti più delicati nella vita di un software, e uno di quelli che i team sottovalutano di più. Aggiungere una colonna, cambiare un tipo, rimuovere una tabella: in sviluppo sono operazioni banali. In produzione, su dati veri che i clienti stanno usando, un passo falso può perdere informazioni in modo irreversibile.
EF offre un meccanismo ordinato per gestire questi cambiamenti in modo versionato e tracciabile. Ma lo strumento non basta: serve la disciplina di usarlo con metodo. Un architetto non genera un cambiamento e lo applica alla cieca sperando che vada bene. Prevede il percorso: verifica in anticipo che il cambiamento non distrugga dati, lo testa su uno scenario che assomiglia alla produzione, prepara una via di ritorno se qualcosa va storto, e quando possibile procede a piccoli passi retrocompatibili invece di un unico salto rischioso.
La differenza tra un team che ha pensato a questo e uno che improvvisa è la differenza tra un aggiornamento che fila liscio e uno che manda in tilt i dispositivi di un cliente. È una di quelle competenze che non si vedono finché non servono, e quando servono valgono l'intero progetto.
Per gli sviluppatori
Non applicate mai un cambiamento allo schema alla cieca. Verificate che non distrugga dati, testatelo su uno scenario simile alla produzione e preparate una via di ritorno. In produzione, sui dati veri dei clienti, non c'è il tasto annulla.
Per l'imprenditore
Un aggiornamento del database fatto male può perdere dati dei clienti in modo irreversibile, soprattutto su prodotti già installati sul campo. Un team con disciplina qui ti protegge da un tipo di incidente che, se accade, non si ripara.
Sapere quando EF non è la risposta.
Chi difende EF in ogni situazione si smaschera davanti a un interlocutore competente.
Adesso faccio una cosa che chi vende strumenti non fa quasi mai. Ti dico dove lo strumento di cui ti sto parlando conviene metterlo da parte. Chi lo difende sempre e comunque si smaschera davanti al primo tecnico competente che incontra, e tu ti ritrovi ad aver comprato una fede al posto di una scelta. Le situazioni qui sotto sono quelle in cui lo accantono io per primo, e riconoscerle è ciò che rende credibile chiunque te ne parli.
Un segno infallibile di maturità tecnica è la capacità di dire dove il proprio strumento preferito non serve. Chi difende EF in ogni situazione, come se fosse sempre e comunque la scelta giusta, si smaschera da solo davanti a un interlocutore competente. EF è un'ottima scelta di default per la grande maggioranza del lavoro, ma non è una religione.
Ci sono casi in cui la strada giusta è un'altra. Per certe letture critiche, dove ogni millisecondo conta e la query gira migliaia di volte, uno strumento più leggero e diretto può affiancare EF e fare meglio proprio quel pezzo. Per inserimenti massivi di grandi quantità di dati, i meccanismi dedicati battono l'ORM senza confronto. Ci sono situazioni in cui una procedura scritta direttamente nel database, magari perché c'è già e funziona da anni, va semplicemente richiamata invece di riscritta.
L'architetto non vede questi casi come un tradimento di EF, ma come la naturale conseguenza di scegliere lo strumento giusto per il compito giusto. È anche ciò che rassicura un cliente serio: un fornitore che ti dice che EF risolve tutto sta o mentendo o non conoscendo abbastanza. Uno che ti dice dove EF eccelle e dove conviene affiancargli altro sta dimostrando di aver visto abbastanza progetti da conoscere i confini.
Per gli sviluppatori
Non innamoratevi dello strumento. EF copre benissimo la maggior parte del lavoro, ma per gli hot path critici, i caricamenti massivi e certi casi limite esistono strade migliori. Saperle riconoscere vi rende più forti, non meno fedeli.
Per l'imprenditore
Diffida di chi ti dice che un solo strumento risolve tutto. La competenza vera si vede in chi conosce i confini e sceglie caso per caso: è il segno che ha visto abbastanza progetti da sapere dove le cose si rompono.
Ogni tema qui è una questione di soldi travestita da tecnica.
Fin qui abbiamo parlato di tecnica. Adesso parliamo della cosa che ti riguarda davvero, cioè quanto ti sta costando tutto questo mentre lo stai leggendo. Una query lenta sono ore di sviluppatori più clienti che si lamentano, e un dato sbagliato in silenzio è la fiducia che si sgretola senza che nessuno ti avvisi. Nessuna di queste voci comparirà mai nel tuo conto economico, e tu le paghi ogni mese lo stesso. Quello studio legale ha pagato in un colpo solo quello che aveva risparmiato per anni.
Una query lenta è ore di sviluppatori più clienti insoddisfatti. Un bug di concorrenza è giornate perse a cercare un fantasma. Una migrazione fatta male è un incidente che nei casi peggiori non si ripara. Una competenza concentrata in una sola persona è un rischio di continuità che nessun imprenditore accetterebbe se lo vedesse scritto in bilancio.
Il debito tecnico sulla parte dati è il più insidioso proprio perché non compare da nessuna parte. Non c'è una riga di conto economico che dica quanto stai pagando per un data layer costruito senza controllo. Eppure lo paghi ogni giorno.
EF nell'era dell'AI
L'AI scrive codice sul data layer a una velocità che nessun team eguaglia. Ma un ORM usato male, per mano di un modello, sbaglia più in fretta di un giunior distratto. Governarla è la nuova competenza da architetto.
Usare l'AI con EF senza spegnere il cervello.
Accelera chi sa già leggere il SQL. Affossa chi si fidava a scatola chiusa.
Adesso arriva la parte che nessuno ti ha ancora spiegato fino in fondo. L'intelligenza artificiale scrive codice per i tuoi dati a una velocità che nessuna squadra umana raggiunge, e sbaglia con la stessa identica velocità con cui fa bene. Oggi affianco otto o dieci aziende l'anno, e la differenza tra chi con l'AI ci guadagna e chi si fa male non sta nel modello che ha scelto. Sta in chi lo guarda lavorare.
Un modello genera in pochi secondi query LINQ, configurazioni del modello e migrazioni. Il problema è che genera anche gli stessi difetti di uno sviluppatore alle prime armi, solo più in fretta e in quantità: query a catena da caricamento pigro, caricamenti dell'entità intera dove bastava una proiezione, tracking attivo su letture di sola visualizzazione. L'AI non elimina gli errori del data layer, li produce alla stessa velocità con cui produce tutto il resto.
Il modo corretto di usarla non è dirle "scrivimi il livello dati" e fidarsi. È trattarla come un pair che propone mentre tu resti l'architetto che valida. Le chiedi la query, e subito dopo le chiedi il SQL che EF ne ricava, il piano che si aspetta, su quali indici conta. Un buon modello sa spiegarti cosa tradurrà, ma solo se glielo chiedi e se tu sai riconoscere quando la spiegazione non regge. La competenza dei primi dieci capitoli non diventa inutile con l'AI: diventa il filtro senza cui l'AI è pericolosa.
Per gli sviluppatori
Fatevi generare la query, poi fatevi generare il SQL e il piano che produce, e leggeteli. Usate l'AI per andare più veloci sui punti che già capireste da soli, non per delegare quelli che non capite.
Per l'imprenditore
L'AI moltiplica la produttività di un team competente e moltiplica i danni di uno che non lo è. Il ritorno sull'AI dipende dal livello del team che la guida: è lì che conviene investire.
Come l'AI perde dati, e come glielo impedisci.
Il rischio peggiore non è la query lenta: è la migrazione distruttiva scritta in un secondo.
Se di tutta questa pagina devi ricordare una cosa sola, ricorda questa. Chiedi all'AI di togliere un campo che non serve più, e lei, con la stessa naturalezza con cui scriverebbe una mail, cancella una colonna piena di dati veri. In sviluppo non se ne accorge nessuno, perché lì dentro non c'è niente da perdere. In produzione è irreversibile, esattamente come le pratiche di quello studio. Il rimedio esiste, costa pochi minuti, e nella maggior parte delle aziende in cui entro non lo applica nessuno.
Il pericolo numero uno è la migrazione dello schema. A un modello che chiedi "togli il campo scaduto" genera senza esitare uno script che fa un DROP COLUMN su dati veri, o un cambio di tipo che tronca in silenzio. In sviluppo non se ne accorge nessuno, in produzione è irreversibile. Il secondo pericolo sono gli aggiornamenti di massa: un ExecuteUpdate o un ciclo di SaveChanges generato con il filtro sbagliato tocca più righe di quante volevi, e quando te ne accorgi il dato corretto non c'è più.
Non serve vietare all'AI di scrivere migrazioni: basta metterle lo stesso cancello che metteresti a un umano. Ogni migrazione generata va letta nel suo SQL, provata su una copia con dati realistici, e resa retrocompatibile in due passi: prima si smette di usare la colonna e si rilascia, poi in una migrazione successiva la si rimuove. L'AI può perfino aiutarti a farlo: le chiedi di riscrivere la migrazione in forma non distruttiva e di generarti lo script di rollback. Gli aggiornamenti di massa si revisionano con il WHERE esplicito sotto gli occhi e si provano in transazione su un ambiente copia prima di toccare la produzione.
Per gli sviluppatori
Non applicate mai una migrazione generata senza leggerne lo SQL e provarla su una copia con dati. Chiedete al modello la versione non distruttiva a due passi e lo script di rollback: sa produrli, se glieli chiedete.
Per l'imprenditore
La perdita di dati generata dall'AI è lo stesso incidente di sempre, solo più rapido da innescare. La protezione sta nella disciplina di revisione e prova su copia prima della produzione.
Dare all'AI le chiavi dei dati senza il permesso di distruggerli.
Non si sceglie tra AI cieca e AI onnipotente. Si mettono guardrail.
A questo punto la reazione istintiva è chiudere tutto, togliere all'AI ogni accesso ai dati e dormire sereni. È l'errore uguale e contrario, perché le stai togliendo il valore proprio dove ce l'ha. Esiste una terza strada, ed è la stessa che useresti con una persona nuova che entra in azienda il primo giorno. Le dai le chiavi di ciò che le serve, e non le dai il permesso di rompere.
Dopo il primo spavento la tentazione è chiudere: niente accesso al database per l'AI. È l'errore uguale e contrario, perché le togli l'utilità proprio dove serve di più, cioè capire i dati reali, riprodurre un bug, profilare una query lenta. La strada giusta è mettere l'AI su binari: accesso sì, ma controllato.
In concreto: l'AI lavora su un database usa e getta o su una copia anonimizzata, mai in scrittura diretta sulla produzione. Le credenziali che le dai sono a minimo privilegio, sola lettura dove basta. Tutto ciò che tocca lo schema o i dati passa da una revisione umana e da un ambiente di prova. Un secondo livello di guardrail vive nel codice e nel database, e regge anche quando il modello genera qualcosa di sbagliato: transazioni esplicite sui passi multipli, la chiave di concorrenza del capitolo 05 che fa fallire una scrittura in conflitto invece di lasciarla passare, e i vincoli a livello di database (chiavi, unicità, foreign key, check) che l'AI non può aggirare nemmeno per errore. Un database ben modellato è l'ultima rete di sicurezza contro l'AI, esattamente come lo è contro l'errore umano.
Per gli sviluppatori
Date all'AI credenziali a minimo privilegio su un DB copia, non sulla produzione. Lasciate che il database faccia il suo lavoro: vincoli, chiavi e transazioni sono la rete che regge anche quando il codice generato sbaglia.
Per l'imprenditore
Escludere l'AI dai dati vi toglie valore; darle accesso libero vi espone. La risposta corretta è la stessa che dareste a un nuovo assunto: accesso controllato, revisione e permessi minimi.
Far leggere all'AI dati reali senza violare la privacy.
Il dato personale che finisce in un prompt esce dal tuo perimetro, e sotto GDPR è un trattamento.
C'è una domanda che mi fanno quasi tutti gli imprenditori e quasi nessun tecnico. Dove finiscono i miei dati, quando qualcuno li dà in pasto all'intelligenza artificiale? La risposta scomoda è che escono dalla tua azienda, e sotto il GDPR quello è un trattamento, con un nome e un responsabile. Si può fare in modo pulito, e si può fare in un modo che un giorno ti presenta il conto.
Quando dai a un modello dati veri per farti aiutare, una riga che riproduce un bug o un dataset per un test, stai potenzialmente inviando dati personali a un servizio esterno. È a tutti gli effetti un trattamento ai sensi del GDPR: serve una base giuridica, vale il principio di minimizzazione, e conta dove finiscono quei dati e per quanto tempo restano.
La regola pratica è non far mai uscire dati personali veri dal perimetro. Prima di passare qualcosa all'AI si anonimizza o si pseudonimizza: nomi, email, codici fiscali e indirizzi diventano valori finti ma plausibili. Per riprodurre un bug non serve il cliente vero, serve la forma del dato. EF stesso aiuta, perché puoi generare seed sintetici realistici o mascherare i campi in una proiezione prima di esportare. Quando il caso d'uso richiede davvero dati reali, come una RAG sui documenti aziendali, la scelta del fornitore diventa una scelta di conformità: modelli che girano nella tua region europea, contratti che escludono l'uso dei tuoi dati per l'addestramento, retention azzerata. In ambito .NET questo significa in pratica preferire un endpoint come Azure OpenAI nella region UE a un'API pubblica generica, e trattare le chiavi come segreti, mai in chiaro nel codice o nei log.
Per gli sviluppatori
Anonimizzate o generate dati sintetici prima di darli al modello. Se vi servono dati reali, usate un fornitore in region europea con niente training sui vostri dati, e tenete le chiavi nei secret.
Per l'imprenditore
Un dato personale mandato con leggerezza a un servizio AI è un rischio legale e reputazionale, non solo tecnico. La conformità qui è una scelta di fornitore e di processo, e va fatta prima, non dopo un controllo.
Claude, Codex o entrambi: scegliere per il lavoro sui dati.
La domanda giusta non è quale è il migliore, ma quale per quale compito.
Ti risparmio la guerra di religione su quale modello sia il migliore, anche perché la risposta cambia ogni pochi mesi. Nelle aziende che seguo li faccio lavorare in coppia, uno che scrive e uno che ha il compito esplicito di trovare i difetti del primo. È la stessa cosa che pretenderesti tra due persone, quando il lavoro tocca i soldi.
Non è una gara con un vincitore unico. I modelli di frontiera, la famiglia Claude, i modelli dietro Codex e gli altri, sono tutti forti su C# ed EF e cambiano di mese in mese. Legarsi a uno solo per fede è lo stesso errore del legarsi a un ORM per fede: una posizione che ti costa quando il contesto cambia.
Un approccio maturo li usa per i loro punti di forza e li fa controllare a vicenda. Un modello genera la query o la migrazione, un altro fa da revisore critico con il compito esplicito di trovarci i difetti. È molto più difficile che due modelli diversi commettano e confermino lo stesso errore, e sul data layer questo secondo parere intercetta proprio le query a catena e le migrazioni distruttive che un singolo modello lascia passare. La scelta concreta dipende da vincoli reali: dove girano i dati per la conformità, quanto costa per token il volume che generi, quanto bene si integra con il tuo ambiente .NET, l'IDE e la pipeline. Molti team seri ne tengono due, uno principale che genera e uno indipendente che rivede: lo stesso doppio controllo che pretenderesti tra due sviluppatori su codice che tocca i dati.
Per gli sviluppatori
Usate un modello per generare e un altro, con istruzione di essere critico, per rivedere. Il disaccordo tra due modelli sul data layer è spesso il punto dove si nasconde il bug.
Per l'imprenditore
Conta come li si combina, più di "quale abbonamento". Due modelli che si controllano a vicenda costano poco e prevengono l'errore costoso: la logica è quella dell'assicurazione, non del gadget.
I test che l'AI scrive bene, e quelli che deve scrivere sotto dettatura.
È velocissima a scrivere test. È proprio questo il rischio.
L'intelligenza artificiale è bravissima a scrivere i test, ed è proprio questo che dovrebbe metterti in guardia. Perché sa scrivere benissimo anche quelli che passano sempre. Qui sotto ti spiego quali test le puoi delegare senza pensarci due volte, e quali devi decidere tu prima di lasciarla lavorare.
L'AI è ottima a coprire il tedioso: i casi limite di una funzione pura, le tabelle di input e output, i test di validazione. Su questo va sfruttata senza esitare, perché è il lavoro che gli umani saltano per noia e che lascia buchi. Sul data layer, però, i test che contano sono quelli che il modello da solo tende a non scrivere: i test di integrazione contro un database reale usa e getta, un container SQL Server effimero avviato per il test, che verificano il SQL vero, gli indici, i vincoli e la concorrenza. La finta memoria che l'AI propone di default è comoda e ingannevole, per gli stessi motivi del capitolo 07: non è il database vero.
Il modo corretto è dettarle la strategia, non delegargliela. Tu decidi cosa va testato contro un DB reale e cosa basta in isolamento, il modello scrive i test dentro quei binari. E i test che proteggono i dati, quelli che verificano che una migrazione non distrugga e che una scrittura concorrente fallisca come deve, li specifichi tu e li guardi funzionare almeno una volta con i tuoi occhi.
Per gli sviluppatori
Fatele scrivere i test noiosi in isolamento, ma dettatele voi i test di integrazione su un DB reale effimero. Sui test che proteggono i dati non delegate il cosa, solo il come.
Per l'imprenditore
Più test non significa più sicurezza se sono i test sbagliati. Il valore è nella strategia, che è competenza umana: l'AI la esegue in fretta, non la sostituisce.
L'AI può far passare i test rompendo l'app. Come te ne accorgi.
Un test verde scritto dall'AI non è una prova: a volte è la prova che si è arresa.
Adesso il pezzo che quasi nessun fornitore ha il coraggio di raccontarti. Se dici a un'AI di far passare i test, a volte lei non aggiusta il programma, aggiusta il test. La barra diventa verde, la riunione finisce contenta, e l'applicazione è rotta. Ci sono controlli semplici che rendono impossibile questo trucco, e te li lascio qui sotto perché tu possa chiederli.
È un rischio concreto e sottile. Un agente a cui dici "fai passare i test" può prendere la scorciatoia sbagliata: invece di correggere il codice, indebolisce l'asserzione, commenta il caso che fallisce, sostituisce con un mock proprio la parte che doveva verificare, o adatta il test al comportamento rotto. Il risultato è una barra verde che certifica il nulla, mentre l'applicazione in produzione è rotta e nessuno lo sa.
La difesa è di metodo, e vale per l'AI come per un collaboratore poco scrupoloso. Primo: test e codice si revisionano nella stessa diff, e si legge cosa è cambiato nei test, non solo che passano. Se una modifica "sistema un bug" ma per farlo cancella asserzioni o aggiunge mock, è un campanello d'allarme. Secondo: si scrive prima il test che deve fallire, si verifica che fallisca davvero sul codice rotto, e solo dopo si lascia che l'AI lo faccia passare, perché un test che non hai mai visto rosso non dimostra niente. Terzo: metriche che il modello non controlla mentre lavora, come il mutation testing (in .NET con Stryker.NET) e la copertura sui rami critici, più una verifica end to end sul comportamento reale e non sulla singola unità isolata.
Per gli sviluppatori
Leggete la diff dei test, non solo il verde. Fate fallire il test prima di farlo passare, e affiancate mutation testing e verifica end to end: sono le misure che un agente non può addomesticare mentre lavora.
Per l'imprenditore
Una suite di test verde non è di per sé una garanzia se chi la scrive può anche indebolirla. La garanzia è il processo di revisione, non il colore della barra.
RAG e ricerca vettoriale: dove EF finisce e comincia il vettore.
Il database relazionale e l'indice vettoriale sono due cose, e vanno tenute coerenti.
Ultimo pezzo, e riguarda quella cosa che ti hanno proposto come se fosse magia. Far rispondere l'intelligenza artificiale sui documenti della tua azienda. Funziona davvero, a una condizione che nessuno ti dice quando te la vende. Se i tuoi dati cambiano e le copie che l'AI legge non cambiano insieme a loro, il sistema ti risponderà con grande sicurezza usando informazioni vecchie, e questo è peggio che non averlo affatto.
La RAG, il recupero aumentato dalla generazione, serve a far rispondere un modello sui tuoi dati reali senza riaddestrarlo: recuperi i pezzi rilevanti e glieli passi nel contesto. Il cuore è la ricerca per similarità su vettori, gli embedding, che è una cosa diversa dalle query di EF: non cerchi per uguaglianza, cerchi per vicinanza di significato. Sono due modi di interrogare che convivono, non uno che sostituisce l'altro.
Il punto architetturale è dove vivono i vettori. Con SQL Server oggi puoi tenere gli embedding vicino ai dati e, con le versioni recenti di EF Core, mappare il tipo vettoriale e interrogarlo, tenendo relazionale e vettoriale nello stesso database e nella stessa transazione: meno pezzi, meno disallineamento. In alternativa usi un indice vettoriale dedicato come Qdrant, pgvector o Azure AI Search, più potente sui grandi volumi, al prezzo di un secondo sistema da mantenere sincronizzato con la fonte di verità relazionale. La fonte di verità resta il database relazionale, i vettori sono un indice derivato. Quando un dato cambia, il suo embedding va rigenerato, altrimenti la RAG risponde su informazioni vecchie con la sicurezza di quelle nuove. Chi tratta l'indice vettoriale come una copia che si aggiorna da sola costruisce lo stesso debito silenzioso delle letture senza controllo, solo travestito da AI.
Per gli sviluppatori
Tenete il relazionale come fonte di verità e rigenerate gli embedding quando i dati cambiano. Valutate i vettori dentro SQL Server prima di aggiungere un secondo sistema da sincronizzare.
Per l'imprenditore
Una RAG che risponde su dati vecchi è peggio di nessuna RAG, perché sbaglia con sicurezza. La coerenza tra dati e vettori è un requisito, non un dettaglio da sistemare dopo.
Le domande che ti fanno dire: so governare Entity Framework.
Tecniche, architetturali, di merito. Nessuna ha una risposta da manuale. Aprile: per ognuna trovi la risposta diretta, come la darebbe chi EF lo governa da anni.
Le dieci sezioni servono a pensare. Le domande a mettersi alla prova.
Le prime dieci sezioni servono a costruire il ragionamento: non un elenco di metodi, ma la doppia lettura, tecnica e di business, per capire cosa una scelta su EF significa sia per chi scrive il codice sia per chi ne paga le conseguenze. Le domande servono a verificare quanto di quel ragionamento è davvero tuo: non si memorizzano, si interiorizzano, così quando il tema si presenta lo riconosci e sai da dove partire.
La regola che vale su tutte: non fingere mai. Una risposta onesta come "dipende dal contesto" oppure "questo lo verifico sui dati veri" vale più di una sicurezza recitata. Sui dati e sulle misure, non sulle opinioni, si costruisce la competenza che regge nel tempo.
La differenza tra chi conosce EF e chi è un esperto non è sapere più metodi. È vedere EF come un pezzo di un sistema, saperlo collegare a database, architettura, runtime e costi reali del progetto, e sapere quando serve altro. La competenza si dimostra collegando, non elencando, e si vende traducendo ogni scelta tecnica nel costo che fa risparmiare a chi ti sta di fronte.
Guardiamolo sul tuo caso, non in teoria.
Se ti stai chiedendo chi ti ha scritto tutto questo, sono Matteo Migliore. Ho fondato Sviluppatore Migliore, lavoro su .NET da ventisei anni e LegalDesk, il gestionale per studi legali che ho costruito, l'ho venduto a SEAC SpA nel 2022. Oggi affianco otto o dieci aziende l'anno per rimettere in ordine il software che hanno già, senza rifarlo da capo.
Se leggendo queste pagine ti è venuto il sospetto che qualcosa di simile stia succedendo anche da te, sappi che è esattamente quello che vado a cercare quando entro in un'azienda. Prendiamoci mezz'ora e lo guardiamo insieme sul tuo caso concreto, con i tuoi numeri davanti invece che con gli esempi di qualcun altro.
