Entity Framework Core 10 a accompagné la sortie de .NET 10 LTS en novembre 2025. La version stable au printemps 2026 est 10.0.8 (mai 2026). Cette release apporte trois changements structurants pour la production : support natif du type vector (Azure SQL DB et SQL Server 2025) pour les workloads IA et recherche sémantique, opérateurs LINQ LeftJoin et RightJoin de première classe, types complexes améliorés avec mise à jour partielle, et filtres nommés (named query filters) qui remplacent enfin le filtre global unique restrictif. Ce tutoriel reprend la mise en place complète d’EF Core 10 dans une application ASP.NET Core 10 : configuration, migrations, requêtes, performances et chaîne de production.
Prérequis
- .NET 10 LTS installé (SDK 10.0.x)
- SQL Server 2022+ (ou PostgreSQL 16+, MySQL 8.4+, SQLite, Azure Cosmos DB)
- Projet ASP.NET Core ou console créé (cf. Installer .NET)
- Notions C# (cf. C# 14 features)
- Temps estimé : 90 minutes
Étape 1 — Installer EF Core 10 et déclarer le DbContext
EF Core 10 est distribué en plusieurs paquets NuGet : Microsoft.EntityFrameworkCore pour le cœur, Microsoft.EntityFrameworkCore.SqlServer (ou autres providers) pour la base, Microsoft.EntityFrameworkCore.Design pour les migrations en dev, et Microsoft.EntityFrameworkCore.Tools pour la CLI dotnet ef.
dotnet add package Microsoft.EntityFrameworkCore --version 10.0.8
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 10.0.8
dotnet add package Microsoft.EntityFrameworkCore.Design --version 10.0.8
dotnet tool install --global dotnet-ef --version 10.0.8
# Vérification
dotnet ef --version
La déclaration du DbContext reste classique. EF Core 10 conserve une rétrocompatibilité presque totale avec EF Core 8/9, ce qui rend la migration triviale.
using Microsoft.EntityFrameworkCore;
public class CatalogueContext : DbContext
{
public CatalogueContext(DbContextOptions<CatalogueContext> options) : base(options) { }
public DbSet<Produit> Produits => Set<Produit>();
public DbSet<Commande> Commandes => Set<Commande>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Produit>(e =>
{
e.HasKey(p => p.Id);
e.Property(p => p.Nom).HasMaxLength(200).IsRequired();
e.HasIndex(p => p.Reference).IsUnique();
});
}
}
Étape 2 — Enregistrer le DbContext dans ASP.NET Core 10
Dans Program.cs (Minimal Hosting depuis .NET 6), on enregistre le contexte avec le provider et la chaîne de connexion lue depuis appsettings.json ou les variables d’environnement.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<CatalogueContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("Catalogue"),
sql =>
{
sql.EnableRetryOnFailure(maxRetryCount: 3);
sql.CommandTimeout(30);
})
.EnableSensitiveDataLogging(builder.Environment.IsDevelopment())
.UseSnakeCaseNamingConvention()
);
var app = builder.Build();
Trois pratiques structurantes pour la production. EnableRetryOnFailure active la retry policy intégrée pour les erreurs transitoires (idéal en cloud où une connexion peut se fermer brièvement). EnableSensitiveDataLogging n’est activé qu’en dev — sinon les valeurs SQL apparaissent dans les logs. UseSnakeCaseNamingConvention() (extension fournie par le package community EFCore.NamingConventions 10.0.1, à ajouter en NuGet — pas built-in EF Core) traduit automatiquement NomProduit en nom_produit côté base, ce qui suit la convention SQL idiomatique sans [Column("...")] partout.
Étape 3 — Migrations avec dotnet ef
Les migrations capturent les changements de schéma sous forme de scripts versionnés. La CLI dotnet ef génère et applique ces scripts. C’est le mécanisme standard pour évoluer une base sans casser les données existantes.
# Créer une migration
dotnet ef migrations add InitialCreate
# Appliquer à la base de dev
dotnet ef database update
# Générer un script SQL idempotent pour la production
dotnet ef migrations script --idempotent --output migrations.sql
# Rollback de la dernière migration (avant déploiement uniquement)
dotnet ef migrations remove
# Lister les migrations
dotnet ef migrations list
Le pattern en production : on génère le script SQL idempotent en CI, on l’inspecte (revue de PR sur le SQL généré), et on l’applique en production via le pipeline de déploiement. Cette approche découple le déploiement du code de la migration du schéma, et permet de rollback indépendamment. Le flag --idempotent rend le script sûr à rejouer plusieurs fois — utile en cas de retry de déploiement.
Étape 4 — Requêtes LINQ avec LeftJoin et RightJoin
EF Core 10 introduit les opérateurs LINQ LeftJoin et RightJoin en première classe, traduits en SQL LEFT JOIN / RIGHT JOIN natifs. Avant, on simulait avec GroupJoin + SelectMany + DefaultIfEmpty, syntaxe verbeuse et parfois mal optimisée. Désormais, c’est limpide.
// Tous les clients avec leur dernière commande (LEFT JOIN moderne)
var resultats = context.Clients
.LeftJoin(
context.Commandes,
c => c.Id,
cmd => cmd.ClientId,
(c, cmd) => new
{
Client = c.Nom,
DateCommande = (DateTime?)cmd.Date,
Montant = (decimal?)cmd.Montant
}
)
.OrderBy(r => r.Client)
.ToListAsync();
// Avec projection sur record (immutable)
public record CommandeResume(string Client, DateTime? Date, decimal? Total);
Les nullables explicites (DateTime?) et (decimal?) signalent au compilateur que le côté droit du LEFT JOIN peut être absent — comportement attendu. EF Core 10 traduit cela en T-SQL idiomatique avec OUTER APPLY ou LEFT JOIN selon le plan optimal. Pour vérifier la requête générée, activez EnableSensitiveDataLogging en dev, ou utilisez .ToQueryString() sur l’IQueryable pour afficher le SQL sans exécuter.
Étape 5 — Types complexes et mise à jour partielle
EF Core 8 a introduit les complex types (équivalent moderne des owned types simplifiés). EF Core 10 les rend complètement utilisables en production avec support de la mise à jour partielle : seules les colonnes modifiées sont écrites, sans tout réémettre.
[ComplexType]
public record Adresse(string Rue, string Ville, string CodePostal, string Pays);
public class Client
{
public Guid Id { get; set; }
public string Nom { get; set; } = "";
public Adresse AdresseFacturation { get; set; } = new("","","","");
public Adresse AdresseLivraison { get; set; } = new("","","","");
}
// Dans OnModelCreating
modelBuilder.Entity<Client>().ComplexProperty(c => c.AdresseFacturation);
modelBuilder.Entity<Client>().ComplexProperty(c => c.AdresseLivraison);
Les types complexes sont stockés dans la même table que l’entité parent (pas de table séparée comme avec les owned entities), ce qui évite les jointures coûteuses. EF Core 10 corrige plusieurs limitations des versions précédentes : ils peuvent désormais être réutilisés dans plusieurs entités, supportent l’héritage limité, et gèrent les valeurs null par défaut.
Étape 6 — Named query filters (filtres nommés)
Avant EF Core 10, on ne pouvait définir qu’un seul filtre global par entité via HasQueryFilter. Pour combiner soft-delete + multi-tenant + isolation par utilisateur, on devait tout empiler dans une seule expression. EF Core 10 introduit les filtres nommés qu’on peut activer/désactiver indépendamment.
modelBuilder.Entity<Article>()
.HasQueryFilter("SoftDelete", a => !a.EstSupprime)
.HasQueryFilter("Tenant", a => a.TenantId == _tenantProvider.CurrentTenantId);
// Désactiver un filtre spécifique pour une requête admin
var tous = await context.Articles
.IgnoreQueryFilters("SoftDelete") // garde le filtre Tenant
.ToListAsync();
// Désactiver tous les filtres (audit)
var audit = await context.Articles
.IgnoreQueryFilters()
.ToListAsync();
Ce changement débloque des patterns multi-tenants beaucoup plus propres : un filtre par dimension d’isolation, activé/désactivé en fonction du contexte d’appel. Avant, on devait dupliquer des contextes ou écrire des extension methods complexes. La granularité par nom rend la logique explicite et testable.
Étape 7 — Support vector pour recherche sémantique
EF Core 10 supporte nativement le type vector de SQL Server 2025 et Azure SQL Database, exposé via la fonction VECTOR_DISTANCE(). Ce support débloque les workloads IA (embeddings, recherche sémantique, RAG) directement en LINQ sans repasser par un store dédié.
public class Document
{
public Guid Id { get; set; }
public string Contenu { get; set; } = "";
[Column(TypeName = "vector(1536)")] // dimension OpenAI text-embedding
public float[] Embedding { get; set; } = [];
}
// Recherche par similarité cosinus
public async Task<List<Document>> ChercherSimilaires(float[] requete, int top = 10)
{
return await context.Documents
.OrderBy(d => EF.Functions.VectorDistance("cosine", d.Embedding, requete))
.Take(top)
.ToListAsync();
}
Pour les projets qui combinent données métier classiques et embeddings IA, garder tout dans la même base évite les sync entre Postgres + Pinecone/Qdrant. SQL Server 2025 et Azure SQL DB supportent les index HNSW sur ces colonnes vectorielles, ce qui rend la recherche performante même sur des millions de vecteurs.
Erreurs fréquentes
| Symptôme | Cause | Solution |
|---|---|---|
InvalidOperationException au démarrage |
DbContext non enregistré dans DI | Vérifier AddDbContext dans Program.cs |
| Migration échoue : « object already exists » | Migration non synchronisée | Utiliser script --idempotent |
| Requête lente, plan SQL plein de joins | Lazy loading non désiré | Désactiver lazy loading (par défaut OFF), utiliser Include explicite |
| N+1 query problem | Itération sur collection naviguée non chargée | .Include(x => x.Sous) ou projection explicite |
| Mémoire qui croît en service long-running | DbContext singleton | Toujours scoped (par requête HTTP) |
UseSnakeCaseNamingConvention inconnue |
Package EFCore.NamingConventions manquant |
dotnet add package EFCore.NamingConventions |
Foire aux questions
EF Core 10 ou Dapper ?
EF Core 10 pour les projets CRUD typiques avec changement de schéma fréquent. Dapper pour les requêtes SQL complexes ou les chemins très chauds. Les deux coexistent souvent dans une même base.
SQL Server, PostgreSQL ou SQLite ?
PostgreSQL en production cloud (gratuit, plus riche fonctionnellement). SQL Server si écosystème Microsoft majeur. SQLite en dev local et tests. EF Core 10 supporte les trois sans changement de code applicatif.
Comment auditer les requêtes lentes ?EnableSensitiveDataLogging en dev + LogTo + filtre sur durée. En prod : Application Insights ou OpenTelemetry instrumentation EF Core officielle.
Compiled queries ou Async ?
Toujours async pour les API web. Compiled queries via EF.CompileAsyncQuery sur les requêtes les plus exécutées (gains 20-30 %).
Migration de EF6 vers EF Core ?
Migration non-triviale (API différentes). Microsoft fournit un guide officiel. Compter 2-5 jours par projet selon la complexité.
Pour aller plus loin
La couche données en place, l’étape suivante est Worker Services pour les tâches d’arrière-plan ou gRPC pour les services inter-microservices. Vue panoramique : C# et .NET moderne.