Personnaliser le "reverse engineering" d'Entity Framework Core
Le “reverse engineering” d’Entity Framework (Core) génère un ensemble de classes et un DbContext représentant votre modèle de base de données permettant d’accéder à cette dernière. Dans cet article nous allons voir comment on peut personnaliser cette génération automatique et la faire correspondre à nos besoins.
Contexte
Il y a quelque temps, sur Stack Overflow, j’ai répondu à une question de quelqu’un souhaitant ajouter une interface IEntity
à toutes les entités générées par l’outil.
Je vais donc détailler ma réponse dans cet article et expliquer pas à pas comment reproduire l’opération et l’adapter à vos besoins.
Cet article part volontairement du principe que vous avez une expérience certaine avec la génération de DbContext
s avec Entity Framework (Core).
Préparation
La première étape consiste à récupérer les sources de Entity Framework Core depuis GitHub.
Tout ceci est rendu possible parce que depuis .NET Core Microsoft a décidé d’open-sourcer tout le code du framework, sans cette décision nous n’aurions jamais pu récupérer les sources de l’outil et l’adapter à nos besoins. En grand fan de l’open-source que je suis, je ne peux que vous encourager vivement à participer et à vous plonger dans ce mouvement.
|
|
Une fois le code récupéré, et avant toute modification, assurez-vous que le projet builde sur votre machine en exécutant le script de build à racine :
|
|
Normalement, ça devrait fonctionner sans soucis, si cela devait ne pas être le cas, je vous invite à jeter un œil à la doc du projet.
Découverte
Pour commencer, à la racine du repo ouvrez la solution EFCore.Tools.slnf
(oui oui c’est bien .slnf) et découvrez cette belle 😍 arborescence et organisation de solution, c’est magnifique, prenez-en de la graine ! Bref je m’égare…
Ensuite dans le projet EFCore.Design
sous-dossier Scaffolding\Internal
il y a une classe CSharpEntityTypeGenerator
qui va particulièrement nous être utile aujourd’hui.
En effet, c’est cette classe qui va se charger d’écrire toutes les lignes de code qui vont composer nos entités, donc c’est ici qu’il va falloir implémenter les changements que l’on souhaite faire.
Si vous prenez un peu temps, vous pourrez constater qu’il y a d’autres classes très intéressantes dans ce dossier, comme par exemple CSharpDbContextGenerator
qui pourrait nous permettre d’influer sur la génération de la classe DbContext
de notre modèle.
Implémentation
Dans le cadre de notre “besoin” énoncé en introduction, nous voulons ajouter à nos classes générées une interface par défaut IEntity
qui pourrait, par exemple, nous permettre de garantir que chacune de nos entités implémente un champ Id
.
|
|
Pour ce faire, dans la classe CSharpEntityTypeGenerator
dans la méthode GenerateClass([NotNull] IEntityType entityType)
il y la ligne suivante :
|
|
Comme on le voit c’est cette ligne qui va générer l’entête de notre classe :
|
|
Pour ajouter notre interface il nous suffit donc de compléter la ligne avec : IEntity
:
|
|
Voilà, c’est aussi simple que ça (bon vous me direz que le “besoin” n’est pas foufou non plus 👻).
Utilisation
Nous devons maintenant compiler et utiliser cette “version” custom d’Entity Framework Core dans notre projet perso afin de générer les modèles avec cette modification.
Commençons par builder le projet comme vu précédemment :
|
|
Dans votre projet perso, ajouter une référence à la DLL Microsoft.EntityFrameworkCore.Design
nouvellement buildée dans le dossier artifacts\bin\EFCore.Design\Debug\netstandard2.1
du repo EFCore et exécuter la commande permettant de générer nos modèles :
|
|
N’oubliez pas de remplacer Microsoft.EntityFrameworkCore.Sqlite
par votre provider, .SqlServer
pour MS SQL Server par exemple.
-f
permet d’overrider les modèles précédemment crées.-o
permet de placer les modèles dans le dossierModels
.
Si vous n’êtes pas adeptes de la ligne de commande le résultat est le même en passant par la génération via Visual Studio comme vous en avez l’habitude.
Résultat
Tous nos modèles générés implémentent maintenant notre interface comme on l’avait souhaité au départ :
|
|
Conclusion
Nous voilà arrivés au bout de ce petit exemple qui nous a permis de mettre notre petit grain de sel dans la tuyauterie de Entity Framework et ceci grâce au fait que le projet est open-source.
Bien sûr, cet exemple est minimaliste et très vite réalisé mais vous pouvez aller beaucoup plus loin et personnaliser à votre guise la génération de code du scaffolding d’EF Core.
Par exemple, dernièrement, j’ai eu à générer un DBContext
d’une base de données de plus de 200 tables et 3500 champs 😨. Par défaut EF Core mets toute la configuration (le mapping des champs aux propriétés) dans la classe DbContext
.
|
|
Dans mon cas, cela produisait une classe de plus de 10'000 lignes 💩 qui mettait à mal mon instance de Visual Studio. C’est pourquoi j’ai décidé de créer une classe de configuration pour chaque entité :
|
|
Et de simplement l’enregistrer dans le DbContext
|
|
Du coup cela nous fait plus qu’une ligne par entité au lieu d’une (ou même souvent plusieurs) ligne(s) par champ dans le DbContext
.
Bref, tout ça pour dire qu’avec un peu d’imagination et d’huile de coude, vous pouvez adapter le générateur de code à tous vos besoins (même les plus fous).
Happy hacking ! 🤙