Vingt ans de changements ont rendu une partie de nos certitudes obsolètes.
Série — Entity Framework : remettre les compteurs à zéro
Article d'ouverture. Les quatre articles suivants reviendront sur l'origine des ORM, l'histoire d'Entity Framework, la prise en main d'EF Core et les pratiques nécessaires en production.
Le produit dont vous vous souvenez n'existe peut-être plus
Vous avez utilisé Entity Framework il y a dix ou quinze ans. Vous vous souvenez d'un designer, d'un fichier EDMX, de classes générées et de requêtes SQL parfois surprenantes. Vous avez peut-être adopté Dapper, écrit des procédures stockées ou repris le contrôle avec ADO.NET. Un nouveau projet arrive, quelqu'un propose EF Core, et vous pensez déjà connaître la suite.
Le problème est que le produit dont vous vous souvenez n'existe plus vraiment. Son nom a survécu, certaines idées aussi, mais l'architecture, les outils et une grande partie des compromis ont changé.
Le phénomène inverse existe également. Un développeur ayant commencé avec EF Core peut croire que l'expression « Entity Framework » a toujours désigné un ORM léger, distribué par packages NuGet, configuré en C# et naturellement intégré à l'injection de dépendances. Ce n'est pas davantage exact.
Sous un même nom se cachent plusieurs générations technologiques : le modèle conceptuel ambitieux de l'ADO.NET Entity Framework, le designer EDMX, Database First, Model First, Code First, ObjectContext, DbContext, puis une réécriture multiplateforme qui a d'abord perdu de nombreuses fonctions avant de les reconstruire autrement.

Figure 1 — Plusieurs générations de persistance .NET ont coexisté. La flèche représente une évolution des usages, pas une filiation stricte entre tous les produits.
Les critiques anciennes n'étaient pas imaginaires
Il serait malhonnête de réduire les réticences à un manque de formation. Certaines versions d'Entity Framework ont réellement été lourdes. Le modèle conceptuel avait un coût. Les fichiers EDMX pouvaient devenir difficiles à fusionner. Le lazy loading a provoqué une quantité considérable de problèmes N+1. Certaines traductions LINQ étaient incomplètes ou inefficaces. Les migrations ont parfois été traitées comme un bouton magique de déploiement.
Les débuts d'EF Core ont ajouté une autre couche de confusion. La version 1.0 apportait une architecture nouvelle, mais elle ne remplaçait pas fonctionnellement EF6. Dans son annonce de juin 2016, Rowan Miller recommandait encore EF Core principalement pour les nouvelles applications ne nécessitant pas les fonctionnalités absentes. Il reconnaissait aussi que son fournisseur LINQ était encore en maturation [1]. Ce rappel historique explique pourquoi deux avis opposés pouvaient être vrais au même moment : EF Core représentait l'avenir architectural, tandis qu'EF6 restait souvent le choix fonctionnel le plus complet.
Aujourd'hui, juger EF Core uniquement à partir de cette période serait aussi erroné que de juger C# actuel à partir de C# 2.0. Mais l'excès inverse serait tout aussi mauvais : un ORM mature ne dispense pas de connaître SQL, les index, les transactions ou le moteur réellement utilisé.
Quelques certitudes à réexaminer
| Certitude fréquente |
Ce qu'il faut vérifier aujourd'hui |
| « Entity Framework impose un EDMX. » |
EF Core utilise généralement des classes C# et une configuration par conventions ou Fluent API. |
| « Les entités doivent être générées. » |
Elles peuvent être de simples classes sans dépendance à EF Core. |
| « Une requête LINQ charge nécessairement des entités. » |
Une projection peut produire directement le DTO utile. |
| « Tout est suivi en mémoire. » |
Le tracking est contrôlable et inutile pour de nombreuses lectures. |
| « Une mise à jour collective exige de charger toutes les lignes. » |
ExecuteUpdate et ExecuteDelete agissent directement en base. |
| « Le SQL est caché. » |
Les journaux et ToQueryString() permettent de l'inspecter. |
| « EF rend tous les moteurs interchangeables. » |
Les fournisseurs partagent un socle, mais conservent des capacités et des traductions différentes. |
| « Choisir EF interdit SQL ou Dapper. » |
Une application peut combiner ces outils par cas d'usage. |
La documentation actuelle décrit DbContext comme une unité de travail de courte durée et précise qu'il n'est pas thread-safe [2]. Ce simple point élimine déjà deux erreurs historiques : conserver un contexte pendant toute la durée de vie d'une application, ou partager la même instance entre opérations parallèles.
Une première preuve par le code
Voici un modèle EF Core complet, volontairement réduit :
using Microsoft.EntityFrameworkCore;
public sealed class Customer
{
public int Id { get; set; }
public required string Name { get; set; }
}
public sealed class SalesDbContext(
DbContextOptions<SalesDbContext> options)
: DbContext(options)
{
public DbSet<Customer> Customers => Set<Customer>();
}
Sa configuration avec SQLite tient en quelques lignes :
var options = new DbContextOptionsBuilder<SalesDbContext>()
.UseSqlite("Data Source=sales.db")
.Options;
await using var db = new SalesDbContext(options);
// Uniquement pour cette démonstration introductive.
// La série utilisera ensuite de véritables migrations.
await db.Database.EnsureCreatedAsync();
L'insertion et la lecture ne nécessitent ni designer ni fichier de mapping :
db.Customers.Add(new Customer { Name = "Contoso" });
await db.SaveChangesAsync();
var customers = await db.Customers
.AsNoTracking()
.OrderBy(customer => customer.Name)
.ToListAsync();
Ce code ne prouve pas qu'EF Core convient à tous les projets. Il montre seulement que les anciennes représentations — designer, XML, génération omniprésente — ne décrivent plus l'expérience ordinaire.
SQLite pour apprendre, le véritable moteur pour valider
Tous les exemples de cette série utiliseront SQLite. Le choix est pratique : aucun serveur à installer, une base dans un fichier, un fonctionnement identique sous Windows, Linux et macOS, et une remise à zéro immédiate.
Changer de fournisseur commence souvent par un package et une méthode de configuration :
// SQLite — fournisseur Microsoft
options.UseSqlite("Data Source=sales.db");
// SQL Server ou Azure SQL — fournisseur Microsoft
options.UseSqlServer(sqlServerConnectionString);
// PostgreSQL — fournisseur Npgsql
options.UseNpgsql(postgreSqlConnectionString);
// Oracle — fournisseur Oracle
options.UseOracle(oracleConnectionString);
Mais il faut refuser la conclusion simpliste selon laquelle seuls ces appels changeraient. La documentation des fournisseurs rappelle qu'ils peuvent étendre EF Core avec des fonctions propres au moteur [3]. SQLite ne prend notamment pas en charge les schémas, les séquences ni les jetons de concurrence générés par la base. Certaines opérations sur decimal, DateTimeOffset et TimeSpan présentent également des restrictions [4].
La règle de la série sera donc la suivante :
SQLite est notre support de démonstration, pas un simulateur universel de SQL Server, PostgreSQL ou Oracle.
Une application de production doit être testée avec son véritable moteur et son véritable fournisseur EF Core.
Ce que cette série cherche à déterminer
La question n'est pas : « EF Core est-il meilleur que SQL ? » Elle est mal posée. EF Core produit du SQL pour les fournisseurs relationnels et permet lui-même d'exécuter du SQL explicite. Les vraies questions sont plus concrètes :
- Pourquoi les ORM ont-ils été créés ?
- Quel problème Entity Framework cherchait-il initialement à résoudre ?
- Pourquoi Microsoft a-t-il choisi une réécriture ?
- Qu'est-ce qu'EF Core a conservé, abandonné ou reconstruit ?
- Quel est aujourd'hui le code minimal d'une application réaliste ?
- Comment éviter le tracking inutile, le N+1 et les graphes chargés sans limite ?
- Comment inspecter les requêtes générées ?
- Comment gérer les migrations, les transactions et la concurrence ?
- Quand faut-il préférer Dapper ou SQL explicite ?
Cette démarche rejoint une remarque intéressante de la documentation Hibernate : à la question « ORM ou SQL ? », sa réponse courte est « use both » [5]. L'opposition absolue est surtout un débat de chapelle. Un ORM sert à supprimer une plomberie répétitive ; SQL reste le langage et le modèle qu'il faut comprendre.

Figure 2 — L'article d'ouverture donne la carte ; les deux articles suivants expliquent l'histoire ; les deux derniers construisent puis durcissent la pratique.
Le parcours proposé
Partie 1 — Pourquoi avons-nous inventé les ORM ?
Nous repartirons du décalage entre graphes d'objets et données relationnelles. Nous comparerons mapping manuel, Active Record, Data Mapper, ORM complet et micro-ORM, puis replacerons ADO.NET, DataSet, LINQ to SQL et NHibernate dans le paysage .NET.
Partie 2 — D'Entity Framework à EF Core : histoire d'une réécriture
Nous reviendrons sur l'ambition initiale du modèle conceptuel, l'EDMX, Database First, Model First, Code First, DbContext, les migrations et la réécriture EF Core. Le but ne sera pas de réciter toutes les versions, mais d'expliquer les ruptures et leurs raisons.
Partie 3 — EF Core aujourd'hui : du projet vide à SQLite
Nous construirons une petite application de gestion commerciale avec .NET 10, EF Core 10 et SQLite : entités, relations, configuration, migrations, insertions, lectures, projections, modifications et inspection du SQL.
Nous passerons du code qui fonctionne au code maîtrisé : durée de vie du contexte, tracking, N+1, requêtes fractionnées, pagination, opérations ensemblistes, concurrence, transactions, SQL brut, tests et migrations en production.
Le contrat de lecture
Cette série ne cherchera pas à prouver qu'EF Core est toujours le bon choix. Ce serait faux. Elle cherchera à permettre une évaluation fondée sur ce qu'EF Core est devenu, non sur ce qu'Entity Framework était il y a dix ou quinze ans.
Nous commencerons donc par le problème d'origine : pourquoi avons-nous voulu faire correspondre des objets à des tables, et pourquoi cette opération apparemment simple a-t-elle engendré autant de solutions différentes ?
Références
- Rowan Miller, « Announcing Entity Framework Core 1.0 », Microsoft .NET Blog, 27 juin 2016.
- Microsoft Learn, « DbContext Lifetime, Configuration, and Initialization ».
- Microsoft Learn, « Database Providers ».
- Microsoft Learn, « SQLite EF Core Database Provider Limitations ».
- Hibernate ORM, « A Short Guide to Hibernate 7 », section d'introduction sur ORM et SQL.
- Microsoft Learn, « What's New in EF Core 10 ». EF Core 10 est une version LTS publiée en novembre 2025 et prise en charge jusqu'au 10 novembre 2028.
Conclusion partielle
J'ai conçui cette série car moi-même je n'y voyais plus très clair. Peu de mes clients utilisent encore Entity Framework et encore, juste en mode legacy, et très peu utilisent EF Core. En tant que MVP depuis presque 20 ans j'ai connu toutes les versions, et même pire pour moi et mes collègues partageant ce titre, j'ai connu toutes les previews, les breaking changes avant même les releases finales connues du public. Autant dire que des "morceaux" d'Entity Framework, ma mémoire en a entassés des tas et que tout cela devient un bouillie de souvenirs qui ne reflètent pas avec justice ce qu'est EF Core aujourd'hui qui paye le prix de son héritage. C'est avant tout pour y voir clair que je me suis remplongé dans l'histoire d'Entity Framework, sa raison d'être, ses différentes incarnations, et surtout ce qu'est devenu EF Core pour savoir s'il est ou non utilisable et quel est son véritable périmètre. Ainss, cette série d'articles est un voyage dans l'histoire d'un produit, d'une fonctionnalité, de méthodes différentes, pour aboutir à ce que vous devez savoir aujourd'hui d'EF Core, en toute justice et toute exactitude technique.
A la semaine prochaine, même lieu, même heure, pour le prochain volet de cette aventure !
Stay Tuned!