EF Core Database First - Les outils en ligne de commande (2/4)

Brouillon

À venir

0 commentaire

visu2

Les outils en ligne de commande de EF Core, ou EF Core .NET Command-line Tools, sont une extension de la commande (multi-plateforme) dotnet. C'est d'ailleurs avec ces outils que l'on gère notre base de données lorsque l'on fait du Code First : dotnet ef database update, dotnet ef migrations add XXXX, ... Donc, si vous avez installé le SDK, vous avez tout ce qu'il vous faut.

Avant de commencer, il est nécessaire de préciser qu'Entity Framework Core a été conçu pour une approche Code First. De ce fait, tout ce que nous allons voir dans cet article et les suivants, est susceptible d'être modifié voire supprimé. Cependant, il peut arriver que l'on ait besoin, pour une raison ou une autre (base de données d'une appli existante, ...), de faire du Database First avec EF Core.

Dans cette série d'articles EF Core Database First, nous allons aborder différentes thématiques autour du Database First avec Entity Framework Core, dont voici la liste :

  1. Visite guidée
  2. Les outils en ligne de commande ( cet article)
  3. Personnaliser le code généré
  4. Quelques astuces utiles

Les commandes disponibles

Parmi ces outils, celui qui va nous intéresser s'appelle dbcontext : comme son nom l'indique cela permet d'effectuer des opérations sur les contextes EF Core.

Si vous ouvrez une invite de commande (cmd sous Windows) dans le dossier du projet de notre exemple ou d'un projet contenant un dbContext et que vous exécutez la commande dotnet ef dbcontext --help, vous devriez voir, entre autres choses, le résultat suivant :

Usage: dotnet ef dbcontext [options] [command]

Options:
  -h|--help        Show help information
  -v|--verbose     Show verbose output.
  --no-color       Don't colorize output.
  --prefix-output  Prefix output with level.

Commands:
  info      Gets information about a DbContext type.
  list      Lists available DbContext types.
  scaffold  Scaffolds a DbContext and entity types for a database.

Use "dbcontext [command] --help" for more information about a command.

Ce résultat est intéressant parce qu'il nous apprend:

  • que l'on peut obtenir des informations à propos d'un DbContext existant : commande info.
  • que l'on peut lister les DbContext existants dans le dossier-projet courant : commande list.
  • que l'on peut créer un DbContext et les entités à partir d'une base de données : commande scaffold.

Note : les lignes de commandes peuvent être exécutées avec powershell comme avec la Package Manager Console de Visual Studio.

La commande info

Il y a peu de choses à dire sur cette commande, si ce n'est qu'elle affiche les informations concernant le ou les DbContext présents dans un projet.

En exécutant la commande dotnet ef dbcontext info dans notre projet exemple, on peut obtenir le résultat suivant pour un DbContext généré sans option :

Provider name: Microsoft.EntityFrameworkCore.SqlServer
Database name: EFCoreDBFirstMyStore
Data source: (localdb)\mssqllocaldb
Options: None

Dans le cas, ou l'on aurait plusieurs DbContext dans le même projet, il possible de préciser le DbContext en ajoutant l'option -c ou --context suivi par le nom du contexte.

Par exemple, si nous avions généré un DbContext pour chacun des schémas de notre projet exemple, nous pourrions obtenir les informations sur le MyStoreStatsContext (schéma stats) en exécutant la commade suivante : dotnet ef dbcontext info -c MyStoreStatsContext. Nous obtiendrions le résultat suivant :

Provider name: Microsoft.EntityFrameworkCore.SqlServer
Database name: EFCoreDBFirstMyStore
Data source: (localdb)\mssqllocaldb
Options: None

La commande list

Cette commande sert à lister les DbContext présents dans le projet courant. Il suffit d'exécuter la commande suivante : dotnet ef dbcontext list.

Pour reprendre l'exemple précédent avec un DbContext par schéma, nous aurions le résultat suivant, avec notre projet exemple :

EFCoreDBFirst.MyStore.MyStoreContext
EFCoreDBFirst.MyStore.MyStoreStatsContext

La commande scaffold

Ce qui nous intéresse le plus pour du Database First, c'est la commande scaffold mais nous verrons aussi les autres commandes à la fin de l'article.

Afin de voir les options que propose la commande scaffold, nous allons commencer par exécuter la commande dotnet ef dbcontext scaffold --help. Le résultat obtenu devrait ressembler à ceci :

Usage: dotnet ef dbcontext scaffold [arguments] [options]

Arguments:
  <CONNECTION>  The connection string to the database.
  <PROVIDER>    The provider to use. (E.g. Microsoft.EntityFrameworkCore.SqlServer)

Options:
  -d|--data-annotations                  Use attributes to configure the model (where possible). If omitted, only the fluent API is used.
  -c|--context <NAME>                    The name of the DbContext.
  --context-dir <PATH>                   The directory to put DbContext file in. Paths are     relative to the project directory.
  -f|--force                             Overwrite existing files.
  -o|--output-dir <PATH>                 The directory to put files in. Paths are relative to the project directory.
  --schema <SCHEMA_NAME>...              The schemas of tables to generate entity types for.
  -t|--table <TABLE_NAME>...             The tables to generate entity types for.
  --use-database-names                   Use table and column names directly from the database.
  --json                                 Show JSON output.
  -p|--project <PROJECT>                 The project to use.
  -s|--startup-project <PROJECT>         The startup project to use.
  --framework <FRAMEWORK>                The target framework.
  --configuration <CONFIGURATION>        The configuration to use.
  --runtime <RUNTIME_IDENTIFIER>         The runtime to use.
  --msbuildprojectextensionspath <PATH>  The MSBuild project extensions path. Defaults to "obj".
  --no-build                             Don't build the project. Only use this when the build is up-to-date.
  -h|--help                              Show help information
  -v|--verbose                           Show verbose output.
  --no-color                             Don't colorize output.
  --prefix-output                        Prefix output with level.
  

Seules les options -d|--data-annotations, -c|--context, --context-dir, -f|--force, -o|--output-dir, --schema, -t|--table et --use-database-names vont nous intéresser.

Attention, il faut que le projet dans lequel vous exécutez la commande scaffold compile. S'il ne compile pas, la commande ne s'exécutera pas. Nous verrons pourquoi dans le prochain article de la série.

Arguments obligatoires

A minima la commande scaffold requiert de définir des arguments qui sont :

  • la connectionstring pour l'accès à la base de données.
  • le provider à utiliser.

La connectionstring est tout simplement une connectionstring "classique". Il est possible d'utiliser des connectionstring nommées mais nous verrons cela plus tard.

Le provider est le fournisseur permettant d'accéder à la base de données. Il en existe pour beaucoup de moteur de base de données : vous en trouverez une liste non exhaustive ici Database Providers.

Pour notre exemple, nous utiliserons :

-Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True; pour la connectionstring.

-Microsoft.EntityFrameworkCore.SqlServer pour le provider.

Scaffolding sans option

Le scaffolding sans option permet de générer un DbContext et les entités avec les options par défaut. Pour cela, il suffit d'exécuter la commande dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer pour obtenir le résultat suivant :

Scaffolding sans option

On peut constater que :

  • tous les fichiers ont été générés à la racine de notre projet.
  • le DbContext porte le nom de la base de données suffixé par Context.
  • les entités portent le nom de la table correspondante.
  • la connectionstring se retrouve en "dur" dans la méthode OnConfiguring du DbContext.
  • la FluentAPI est utilisée, dans le DbContext, pour définir les entités et leurs propriétés.

Option -d ou --data-annotations

Si vous souhaitez que le code généré utilise les data-annotations plutôt que la FluentAPI, il suffit d'ajouter à la fin de la commande l'option -d ou --data-annotations.

Pour notre projet, la commande sera dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -d ou dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --data-annotations.

On peut constater que des attributs ont été ajoutés sur les propriétés des entités. Cependant, comme l'aide de la commande le précise, les data-annotations sont utilisées quand c'est possible. C'est pour cette raison qu'il reste un peu de FluentAPI dans le DbContext.

Scaffolding data annotations entité

Scaffolding data annotations dbcontext

Option -c ou --context

Si vous souhaitez personnaliser le nom de la classe du DbContext, il suffit d'ajouter à la fin de la commande l'option -c ou --context suivi du nom désiré.

Pour notre projet, nous pourrions choisir comme nom MyStoreContext au lieu de EFCoreDBFirstMyStore. La commande sera dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -c MyStoreContext ou dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --context MyStoreContext.

Voici le résultat obtenu :

Scaffolding context

Option --context-dir

Si vous souhaitez que votre DbContext soit généré dans un dossier différent des entités, il suffit d'ajouter à la fin de la ligne de commande l'option --context-dir suivi du nom du dossier en chemin relatif au dossier du projet.

Pour notre projet, nous pourrions choisir de générer le DbContext dans un dossier nommé Context. La commande serait dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --context-dir Context

Scaffolding context-dir

Il est possible de définir un chemin de plusieurs dossiers : par exemple DAL\Context. L'option deviendra --context-dir DAL\Context pour donner ce résultat :

Scaffolding context-dir with subfolder

Option -f ou --force

Si vous souhaitez mettre à jour un DbContext existant et les entités, il suffit d'ajouter à la fin de la ligne de commande l'option -f ou --force.

Attention, dans le cas où une table aurait été supprimée de la base de données, cela ne supprimera pas le fichier de l'entité correspondante. Dans ce cas, il vaudra mieux supprimer les fichiers et relancer la commande de scaffolding sans cette option.

Pour notre projet, la commande sera dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -f ou dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --force

Option -o ou output-dir

Si vous souhaitez générer votre DbContext et les entités dans un dossier en particulier, il suffit d'ajouter à la fin de la ligne de commande l'option -o ou --output-dir suivi du nom du dossier en chemin relatif au dossier du projet.

Pour notre projet, nous pourrions choisir de générer le DbContext et les entités dans un dossier nommée DAL. La commande serait dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o DAL ou dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --output-dir DAL

Scaffolding output-dir

Il est possible de définir un chemin de plusieurs dossiers : par exemple Generated\DAL. L'option deviendra --context-dir Generated\DAL pour donner ce résultat :

Scaffolding output-dir with subfolder

Option --schema

Si vous avez plusieurs schémas dans votre base de données et que vous ne souhaitez pas tous les importer, il suffit d'ajouter à la fin de la ligne de commande l'option --schema suivi du nom du schéma à importer. Il est possible d'importer plusieurs schémas en ajoutant autant d'options --schema que l'on a de schémas à importer.

Pour notre projet, nous pourrions n'importer que le schéma dbo. La commande serait dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer --schema dbo.

Scaffolding schema

Ici, on peut voir que les tables Category et Product du schéma dbo ont été importées et pas la table ProductView du schéma stats.

Option -t ou --table

Si vous souhaitez importer certaines tables et pas d'autres, il suffit d'ajouter à la fin de la ligne de commande l'option -t ou --table suivi du nom de la table à importer : le nom de la table peut être préfixé par le nom du schéma (schema.table). Il est possible d'importer plusieurs tables en ajoutant autant d'options que l'on a de tables à importer.

Attention, si vous importez des tables qui ont des clés étrangères vers des tables non importées, la commande retournera des warning vous avertissant qu'elle n'a pas pu modéliser la clé étrangère (et la navigation property).

Pour notre projet, nous pourrions choisir d'importer la table Product du schéma dbo et la table ProductView du schéma stats. La commande serait dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=EFCoreDBFirstMyStore;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -t Product -t stats.ProductView

Scaffolding table

Option --use-database-names

Si vous souhaitez que le nom des entités et des propriétés soient identiques à ce qui se trouve en base de données, il suffit d'ajouter à la fin de la ligne de commande l'option --use-database-names.

Par défaut, EF Core "normalise" les noms de tables et les colonnes pour correspondre aux standards de C# : à savoir du camel case. Par exemple, pour une table nommée product_view, une entité nommée ProductView. L'utilisation de l'option --use-database-names permettra de garder le nom product_view.

Scaffolding use-database-names

Jérémy Larrieu

Titulaire d'une licence pro Systèmes Informatiques et Logiciels, Jérémy a d'abord travaillé en tant que développeur web freelance, puis développeur .NET dans une ESN avant de rejoindre SoftFluent.

Profil de l'auteur