Module 5 .NET Thème 33 / 35

Entity Framework et agent

EF abstrait le SQL : l'agent produit du code qui compile mais peut générer des migrations destructives, des problèmes N+1 et des requêtes évaluées côté client. Les garde-fous sont la revue des migrations, l'inspection du SQL généré et les tests d'intégration sur une vraie base.

EF abstrait le SQL, et c’est là le piège

Entity Framework laisse écrire des requêtes en C# fluide et persiste des objets sans toucher à une ligne de SQL. Cette abstraction est confortable, mais elle cache ce qui compte vraiment en production : le schéma réel de la base, le nombre de requêtes émises, le volume de données ramené. Un agent, lui, optimise pour « ça compile et les tests passent ». Il ne voit pas le SQL généré derrière son LINQ, ni l’impact d’une migration sur des données existantes.

Point clé : Le fil rouge de ce thème est que l’agent vise « ça marche », pas « c’est correct et efficace en prod ». Ton rôle est de poser les garde-fous qui font la différence.

Les trois angles morts récurrents : les migrations destructives, le problème N+1, et les requêtes qui basculent en évaluation côté client. Aucun n’est attrapé par le compilateur, et la plupart passent les tests unitaires en mémoire.

Migrations : toujours relire avant d’appliquer

Une migration est du code exécutable qui modifie le schéma d’une base réelle. L’agent en génère une qui « correspond au modèle », mais il ne juge pas de son effet sur les données déjà présentes. Un renommage de propriété peut être interprété comme une suppression suivie d’une création : la colonne disparaît, et les données avec.

Attention — appliquer une migration générée sans la relire, en supposant que « générée sans erreur » égale « préserve les données ». C’est faux : une migration peut parfaitement compiler et détruire une table en prod.

Point clé : Relire le code de la migration pour repérer toute opération destructive (drop de colonne, de table, perte de données) est le garde-fou minimal avant de l’appliquer.

// Generee par l'agent : a relire AVANT d'appliquer
migrationBuilder.DropColumn("Email", "Users");   // perte de donnees ?
migrationBuilder.AddColumn("EmailAddress", "Users");
// Renommage ou drop+create ? Relire et corriger si besoin.

Change tracking : une mécanique pleine de surprises

Le change tracking garde en mémoire l’état des entités chargées par le contexte. À l’enregistrement, EF compare l’état courant à cet instantané et n’émet que les commandes nécessaires : UPDATE des seules propriétés modifiées, INSERT des nouvelles entités. Pratique, mais source de surprises pour qui ne connaît pas le mécanisme.

Point clé : Une entité encore suivie par le contexte peut être persistée même sans intention explicite : modifier un objet chargé, puis enregistrer, écrit en base.

Attention — un contexte à longue durée de vie qui suit des milliers d’entités devient lent et gourmand en mémoire. Pour les lectures pures, un suivi désactivé allège la charge — un détail que l’agent omet souvent.

Évaluation côté client : la requête qui ne se traduit pas

Quand une partie de la requête LINQ n’est pas traduisible en SQL (un appel de méthode C# personnelle dans un filtre, par exemple), le risque est l’évaluation côté client : EF ramène un volume bien plus large de lignes, puis applique le filtre en mémoire. Sur une petite table de dev, invisible. Sur des millions de lignes en prod, catastrophique.

Point clé : Les versions récentes lèvent souvent une exception pour le filtrage non traduisible, mais certaines projections ou regroupements peuvent encore basculer côté client sans erreur. Inspecter le SQL réellement généré reste le seul vrai contrôle.

Le N+1 que l’agent ne voit pas

Le N+1, c’est une requête pour charger N entités parentes, puis une requête supplémentaire par parent pour charger ses enfants — souvent via le lazy loading ou un accès naïf dans une boucle. Le code se lit naturellement, il compile, et le test unitaire sur trois lignes ne voit rien. C’est exactement le « ça compile et les tests passent » dont l’agent se contente.

// N+1 : 1 requete pour les commandes, puis 1 par commande
var commandes = db.Orders.ToList();
foreach (var c in commandes)
    Console.WriteLine(c.Lines.Count);   // requete cachee a chaque tour

Point clé : La seule façon de l’attraper : inspecter le SQL généré (logs EF, sortie console) ou un test d’intégration sur une vraie base qui compte les requêtes émises. Les tests unitaires qui mockent le contexte en mémoire court-circuitent justement la couche qui produit le SQL.

DispositifAttrape un N+1 ?
Compilateur / types C#Non : le N+1 compile parfaitement
Tests unitaires (contexte mocké en mémoire)Non : ils sautent la génération du SQL
Relecture visuelle du LINQPartiellement : aveugle au SQL réel
Tests d’intégration sur vraie base + inspection du SQLOui : compte les requêtes, révèle la bascule côté client

La frontière du DbContext et de l’agrégat

Le repository et la racine d’agrégat existent pour concentrer l’accès aux données en un point unique et garantir les invariants. Quand l’agent disperse des DbContext et des requêtes EF dans les services et les contrôleurs « parce que ça marche », on perd ce point de contrôle : les stratégies de chargement deviennent incohérentes d’un appel à l’autre, les N+1 prolifèrent, et des entités peuvent être modifiées hors des règles de l’agrégat.

Point clé : Pont avec le DDD : le repository est la frontière d’accès aux données. Rappelle-le à l’agent dans le CLAUDE.md — « tout accès EF passe par un repository, jamais de DbContext dans un contrôleur ».

Attention — déléguer l’accès aux données EF à un agent quasi autonome n’est fiable qu’en empilant les filets : repository comme frontière, revue humaine des migrations, tests d’intégration sur vraie base qui inspectent le SQL, et limite de tours. C’est l’application directe du Zero Trust Execution : ne faire confiance à aucune étape.

Quiz — teste tes connaissances
Module 5 7 questions Objectif : 5/7 minimum
0/7
bonnes reponses
Objectif non atteint (minimum 5/7 requis).
Remonte relire la fiche memo ci-dessus en pretant attention aux points rates, puis clique sur « Recommencer » pour retenter.