Génération de modèles automatique (ingénierie à rebours)
L’ingénierie inverse est le processus de génération automatique de classes de type d’entité et d’une classe DbContext basée sur un schéma de base de données. Elle peut être effectuée à l’aide de la commande Scaffold-DbContext
des outils de la console de gestionnaire de package (PMC) EF Core ou de la commande dotnet ef dbcontext scaffold
des outils de l’interface de ligne de commande (CLI) .NET.
Remarque
La génération de modèles automatique d’un DbContext
et des types d’entités documentés ici est distincte de la génération de modèles automatique des contrôleurs dans ASP.NET Core à l’aide de Visual Studio, ce qui n’est pas documenté ici.
Conseil
Si vous utilisez Visual Studio, essayez l’extension de communauté EF Core Power Tools. Ces outils fournissent un outil graphique qui s’appuie sur les outils de ligne de commande EF Core et offre des options de flux de travail et de personnalisation supplémentaires.
Prérequis
- Avant de générer des modèles, vous devez installer soit les outils PMC, qui fonctionnent uniquement sur Visual Studio, soit les outils de l’interface CLI .NET, qui fonctionnent sur toutes les plateformes prises en charge par .NET.
- Installez le package NuGet pour
Microsoft.EntityFrameworkCore.Design
dans le projet dans lequel vous effectuez la génération de modèles automatique. - Installez le package NuGet pour le fournisseur de base de données qui cible le schéma de base de données à partir duquel vous souhaitez générer automatiquement des modèles.
Arguments obligatoires
Les commandes PMC et CLI .NET ont deux arguments requis : utiliser la chaîne de connexion à la base de données et le fournisseur de base de données EF Core.
Chaîne de connexion
Avertissement
Cet article utilise une base de données locale qui ne nécessite pas l’authentification des utilisateurs. Les applications de production doivent utiliser le flux d’authentification le plus sécurisé disponible. Pour plus d’informations sur l’authentification pour les applications de test et de production déployées, consultez Flux d’authentification sécurisés.
Le premier argument de la commande est une chaîne de connexion à la base de données. Les outils utilisent cette chaîne de connexion pour lire le schéma de base de données.
La façon dont la chaîne de connexion est entre guillemets et échappées dépend de l’interpréteur de commandes utilisé pour exécuter la commande. Reportez-vous à la documentation de l’interpréteur de commandes. Par exemple, PowerShell nécessite une $
échappement, mais pas \
.
L’exemple suivant génère des types d’entités et un DbContext
à partir de la base de données Chinook
située sur l’instance LocalDB de SQL Server de la machine, utilisant le fournisseur de base de données Microsoft.EntityFrameworkCore.SqlServer
.
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
Conseil
Vous pouvez utiliser Configuration pour stocker et récupérer le chaîne de connexion
Chaînes de connexion dans le code généré automatiquement
Par défaut, le générateur de modèles automatique inclut la chaîne de connexion dans le code généré automatiquement, mais avec un avertissement. Par exemple :
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");
Cela permet au code généré de ne pas se planter lors de la première utilisation, ce qui serait une expérience d’apprentissage très médiocre. Toutefois, comme l’indique l’avertissement, les chaînes de connexion ne doivent pas exister dans le code de production. Consultez Durée de vie, configuration et initialisation de DbContext pour connaître les différentes façons dont les chaînes de connexion peuvent être gérées.
Conseil
L’option -NoOnConfiguring
(PMC Visual Studio) ou --no-onconfiguring
(CLI .NET) peut être passée pour supprimer la création de la méthode OnConfiguring
contenant la chaîne de connexion.
Nom du fournisseur
Le deuxième argument est le nom du fournisseur. Le nom du fournisseur est généralement identique au nom du package NuGet du fournisseur. Par exemple, pour SQL Server ou Azure SQL, utilisez Microsoft.EntityFrameworkCore.SqlServer
.
Options de ligne de commande
Le processus de génération de modèles automatique peut être contrôlé par différentes options de ligne de commande.
Spécification des tables et des vues
Par défaut, toutes les tables et les vues du schéma de base de données sont générées automatiquement en types d'entités. Vous pouvez limiter les tables et les vues qui sont générées automatiquement en spécifiant des schémas et des tables.
L’argument -Schemas
(PMC Visual Studio) ou --schema
(CLI .NET) spécifie les schémas de tables et de vues pour lesquels les types d’entités seront générés. Si cet argument est omis, alors tous les schémas sont inclus. Si cette option est utilisée, alors toutes les tables et les vues dans les schémas seront incluses dans le modèle, même si elles ne sont pas explicitement incluses à l’aide de -Tables
ou de --table
.
L’argument -Tables
(PMC Visual Studio) ou --table
(CLI .NET) spécifie les tables et les vues pour lesquels les types d’entités seront générés. Vous pouvez inclure des tables ou des vues dans un schéma spécifique à l’aide du format « schema.table » ou « schema.view ». Si cette option est omise, alors toutes les tables et les vues sont incluses. |
Par exemple, pour générer automatiquement uniquement les tables Artists
et Albums
:
dotnet ef dbcontext scaffold ... --table Artist --table Album
Pour générer automatiquement toutes les tables et les vues à partir des schémas Customer
et Contractor
:
dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor
Par exemple, pour générer automatiquement la table Purchases
à partir du schéma Customer
, ainsi que les tables Accounts
et Contracts
à partir du schéma Contractor
:
dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts
Conservation des noms de base de données
Les noms de tables et de colonnes sont corrigés pour mieux correspondre aux conventions d’affectation de noms .NET pour les types et les propriétés par défaut. La spécification de -UseDatabaseNames
(PMC Visual Studio) ou de --use-database-names
(CLI .NET) désactive ce comportement pour conserver autant que possible les noms de bases de données d’origine. Les identificateurs .NET non valides sont quand même corrigés et les noms synthétisés comme les propriétés de navigation restent conformes aux conventions d’affectation de noms .NET.
Par exemple, considérez les tables suivantes :
CREATE TABLE [BLOGS] (
[ID] int NOT NULL IDENTITY,
[Blog_Name] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));
CREATE TABLE [posts] (
[id] int NOT NULL IDENTITY,
[postTitle] nvarchar(max) NOT NULL,
[post content] nvarchar(max) NOT NULL,
[1 PublishedON] datetime2 NOT NULL,
[2 DeletedON] datetime2 NULL,
[BlogID] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);
Par défaut, les types d’entités suivants sont générés automatiquement à partir de ces tables :
public partial class Blog
{
public int Id { get; set; }
public string BlogName { get; set; } = null!;
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
public partial class Post
{
public int Id { get; set; }
public string PostTitle { get; set; } = null!;
public string PostContent { get; set; } = null!;
public DateTime _1PublishedOn { get; set; }
public DateTime? _2DeletedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}
Toutefois, l’utilisation de -UseDatabaseNames
ou de --use-database-names
entraîne les types d’entités suivants :
public partial class BLOG
{
public int ID { get; set; }
public string Blog_Name { get; set; } = null!;
public virtual ICollection<post> posts { get; set; } = new List<post>();
}
public partial class post
{
public int id { get; set; }
public string postTitle { get; set; } = null!;
public string post_content { get; set; } = null!;
public DateTime _1_PublishedON { get; set; }
public DateTime? _2_DeletedON { get; set; }
public int BlogID { get; set; }
public virtual BLOG Blog { get; set; } = null!;
}
Utiliser des attributs de mappage (alias Annotations de données)
Les types d’entités sont configurés à l’aide de l’API ModelBuilder
dans OnModelCreating
par défaut. Spécifiez -DataAnnotations
(PMC) ou --data-annotations
(CLI .NET Core) pour utiliser à la place des attributs de mappage lorsque cela est possible.
Par exemple, l’utilisation de l’API Fluent génère automatiquement les modèles suivants :
entity.Property(e => e.Title)
.IsRequired()
.HasMaxLength(160);
Tandis que l’utilisation d’annotations de données génère automatiquement les modèles suivants :
[Required]
[StringLength(160)]
public string Title { get; set; }
Conseil
Certains aspects du modèle ne peuvent pas être configurés à l’aide d’attributs de mappage. Le générateur de modèles automatique utilise toujours l’API de génération de modèles pour gérer ces cas.
Nom DbContext
Par défaut, le nom de la classe de DbContext
généré automatiquement est le nom de la base de données avec le suffixe Context ajouté. Pour en spécifier un autre, utilisez -Context
dans PMC et --context
dans l’interface CLI .NET Core.
Répertoires et espaces de noms cibles
Les classes d’entité et une classe DbContext sont générées automatiquement dans le répertoire racine du projet et utilisent l’espace de noms par défaut du projet.
Vous pouvez spécifier le répertoire dans lequel les classes sont générées automatiquement à l’aide de --output-dir
, et vous pouvez utiliser --context-dir
pour générer automatiquement le modèle de la classe DbContext dans un répertoire distinct de celui des classes de type d’entité :
dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models
Par défaut, l’espace de noms est l’espace de noms racine plus les noms des sous-répertoires sous le répertoire racine du projet. Toutefois, vous pouvez remplacer l’espace de noms pour toutes les classes de sortie à l’aide de --namespace
. Vous pouvez également remplacer l’espace de noms pour la classe DbContext à l’aide de --context-namespace
:
dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace
Code généré automatiquement
Le résultat de la génération de modèles automatique à partir d’une base de données existante est le suivant :
- Un fichier contenant une classe qui hérite de
DbContext
- Un fichier pour chaque type d’entité
Conseil
À compter d’EF7, vous pouvez également utiliser des modèles de texte T4 pour personnaliser le code généré. Pour plus d’informations, consultez Modèles d’ingénierie à rebours personnalisés.
Types références pouvant accepter la valeur Null C#
Le générateur automatique peut créer un modèle EF et des types d’entités qui utilisent des types référence pouvant accepter la valeur Null C# (NRT). L’utilisation de NRT est automatiquement générée lorsque la prise en charge de NRT est activée dans le projet C# dans lequel le code est généré automatiquement.
Par exemple, la table Tags
suivante contient des colonnes de chaîne pouvant accepter ou non la valeur Null :
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
Cela génère des propriétés de chaîne nullable et non-nullable correspondantes dans la classe générée :
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
De même, la table Posts
suivante contient une relation requise avec la table Blogs
:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));
Cela entraîne la génération de modèles automatique de relation non-nullable (obligatoire) entre les blogs :
public partial class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<Post> Posts { get; set; }
}
Et les publications :
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
Relations plusieurs-à-plusieurs
Le processus de génération de modèles automatique détecte les tables de jointure simples et génère automatiquement un mappage plusieurs-à-plusieurs pour elles. Par exemple, considérez les tables pour Posts
et Tags
, et une table de jointure PostTag
les connectant :
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] int NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);
Lorsque le modèle est généré, cela entraîne une classe pour Post :
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
Et une classe pour Tag :
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
Mais aucune classe pour la table PostTag
. Au lieu de cela, la configuration d’une relation plusieurs-à-plusieurs est générée automatiquement :
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<Dictionary<string, object>>(
"PostTag",
l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
});
Autres langages de programmation
Les packages EF Core publiés par le code C# généré de Microsoft. Toutefois, le système de génération de modèles automatique sous-jacent prend en charge un modèle de plug-in pour la génération de modèles de génération automatique vers d’autres langages. Ce modèle de plug-in est utilisé par différents projets exécutés par la communauté, par exemple :
- EntityFrameworkCore.VisualBasic prend en charge Visual Basic
- EFCore.FSharp prend en charge F#
Personnalisation du code
À compter d’EF7, l’une des meilleures façons de personnaliser le code généré consiste à personnaliser les modèles T4 utilisés pour le générer.
Le code peut également être modifié après sa génération, mais la meilleure façon de procéder dépend de votre intention de réexécuter le processus de génération de modèles automatique lorsque le modèle de base de données change.
Génération de modèles automatique une seule fois
Avec cette approche, le code généré automatiquement fournit à l’avenir un point de départ pour le mappage basé sur le code. Toutes les modifications apportées au code généré peuvent être effectuées comme vous le souhaitez. Il devient du code normal comme n’importe quel autre code dans votre projet.
La synchronisation de la base de données et du modèle EF peut être effectuée de deux manières :
- Passez à l’utilisation de migrations de base de données EF Core et utilisez les types d’entités et la configuration du modèle EF comme source de vérité, en utilisant des migrations pour piloter le schéma.
- Mettez à jour manuellement les types d’entités et la configuration EF lorsque la base de données change. Par exemple, si une nouvelle colonne est ajoutée à une table, ajoutez une propriété pour la colonne au type d’entité mappé et ajoutez toute configuration nécessaire à l’aide d’attributs de mappage et/ou de code dans
OnModelCreating
. Cela est relativement facile, avec comme seul véritable défi d’être un processus pour s’assurer que les modifications de base de données sont enregistrées ou détectées d’une certaine manière afin que le(s) développeur(s) responsable(s) du code puisse(nt) réagir.
Génération de modèles automatique répétée
Une autre approche de la génération de modèles automatique consiste à regénérer automatiquement des modèles chaque fois que la base de données change. Cela remplacera tout code généré précédemment, ce qui signifie que les modifications apportées aux types d’entités ou à la configuration EF dans ce code seront perdues.
[ASTUCE] Par défaut, les commandes EF ne remplacent aucun code existant pour se protéger contre la perte accidentelle de code. L’argument
-Force
(PMC Visual Studio) ou--force
(CLI .NET) peut être utilisé pour forcer le remplacement des fichiers existants.
Étant donné que le code généré automatiquement sera remplacé, il est préférable de ne pas le modifier directement, mais plutôt de s’appuyer sur des méthodes et classes partielles, et les mécanismes dans EF Core qui permettent la substitution de la configuration. Plus précisément :
- La classe
DbContext
et les classes d’entité sont générées comme partielles. Cela permet d’introduire des membres et du code supplémentaires dans un fichier distinct qui ne sera pas substitué lors de l’exécution de la génération de modèles automatique. - La classe
DbContext
contient une méthode partielle appeléeOnModelCreatingPartial
. Une implémentation de cette méthode peut être ajoutée à la classe partielle pour leDbContext
. Elle sera ensuite appelée, après queOnModelCreating
soit appelé. - La configuration du modèle effectuée à l’aide des API
ModelBuilder
remplace toute configuration effectuée par des conventions ou des attributs de mappage, ainsi que la configuration antérieure effectuée sur le générateur de modèles. Cela signifie que le code dansOnModelCreatingPartial
peut être utilisé pour remplacer la configuration générée par le processus de génération de modèles automatique, sans avoir à supprimer cette configuration.
Enfin, n’oubliez pas que à partir d’EF7, les modèles T4 utilisés pour générer du code peuvent être personnalisés. Il s’agit souvent d’une approche plus efficace que la génération de modèles automatique avec les valeurs par défaut, puis la modification avec des classes partielles et/ou des méthodes.
Fonctionnement
L’ingénierie à rebours commence par lire le schéma de base de données. Elle lit les informations sur les tables, les colonnes, les contraintes et les index.
Ensuite, elle utilise les informations de schéma pour créer un modèle EF Core. Les tables sont utilisées pour créer des types d’entités ; les colonnes sont utilisées pour créer des propriétés et les clés étrangères sont utilisées pour créer des relations.
Enfin, le modèle est utilisé pour générer du code. Les classes de type d’entité correspondantes, l’API Fluent et les annotations de données sont générées automatiquement pour recréer le même modèle à partir de votre application.
Limites
- Tout ce qui concerne un modèle ne peut pas être représenté à l’aide d’un schéma de base de données. Par exemple, les informations sur les hiérarchies d’héritage, les types détenus et le fractionnement de table ne sont pas présentes dans le schéma de base de données. Ainsi, ces constructions ne feront jamais l’objet d’une génération automatique.
- De plus, certains types de colonnes peuvent ne pas être pris en charge par le fournisseur EF Core. Ces colonnes ne seront pas incluses dans le modèle.
- Vous pouvez définir des jetons d’accès concurrentiel dans un modèle EF Core pour empêcher deux utilisateurs de mettre à jour la même entité en même temps. Certaines bases de données ont un type spécial pour représenter ce type de colonne (par exemple, rowversion dans SQL Server) auquel cas nous pouvons appliquer l’ingénierie à rebours sur ces informations. Toutefois, les autres jetons d’accès concurrentiel ne feront pas l’objet d’une génération automatique.