Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Toutes les relations un-à-un et un-à-plusieurs sont définies par une clé étrangère sur la fin dépendante qui fait référence à une clé primaire ou alternative à la fin du principal. Pour des raisons pratiques, cette clé primaire ou alternative est appelée « clé principale » pour la relation. Les relations plusieurs-à-plusieurs sont composées de deux relations un-à-plusieurs, chacune d’elles étant définie par une clé étrangère référençant une clé principale.
Conseil / Astuce
Le code ci-dessous se trouve dans ForeignAndPrincipalKeys.cs.
Clés étrangères
La propriété ou les propriétés qui composent une clé étrangère sont souvent découvertes par convention. Les propriétés peuvent également être configurées explicitement à l’aide d’attributs de mappage ou de l’API de génération de modèles HasForeignKey
.
HasForeignKey
peut être utilisé avec une expression lambda. Par exemple, pour une clé étrangère composée d’une propriété unique :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.ContainingBlogId);
}
Ou, pour une clé étrangère composite composée de plusieurs propriétés :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => new { e.ContainingBlogId1, e.ContainingBlogId2 });
}
Conseil / Astuce
L’utilisation d’expressions lambda dans l’API de génération de modèles garantit que l’utilisation de la propriété est disponible pour l’analyse du code et la refactorisation, et fournit également le type de propriété à l’API pour une utilisation dans d’autres méthodes chaînées.
HasForeignKey
peut également recevoir le nom de la propriété de clé étrangère sous forme de chaîne. Par exemple, pour une propriété unique :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("ContainingBlogId");
}
Ou, pour une clé étrangère composite :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("ContainingBlogId1", "ContainingBlogId2");
}
L’utilisation d’une chaîne est utile lorsque :
- La propriété ou les propriétés sont privées.
- La propriété ou les propriétés n’existent pas sur le type d’entité et doivent être créées en tant que propriétés d’ombre.
- Le nom de la propriété est calculé ou construit en fonction d’une entrée au processus de génération de modèle.
Colonnes de clé étrangère non nullables
Comme décrit dans les relations facultatives et requises, la nullabilité de la propriété clé étrangère détermine si une relation est facultative ou obligatoire. Toutefois, une propriété de clé étrangère nullable peut être utilisée pour une relation requise à l’aide de l’attribut[Required]
ou en appelant IsRequired
l’API de génération de modèle. Par exemple:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.IsRequired();
}
Ou, si la clé étrangère est découverte par convention, peut IsRequired
être utilisée sans appel à HasForeignKey
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.IsRequired();
}
Le résultat final est que la colonne de clé étrangère dans la base de données devient non annulable, même si la propriété de clé étrangère est annulable. La même chose peut être obtenue en configurant explicitement la propriété de clé étrangère elle-même en fonction des besoins. Par exemple:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.Property(e => e.BlogId)
.IsRequired();
}
Clés étrangères d'ombre
Les propriétés de clé étrangère peuvent être créées en tant que propriétés d’ombre. Une propriété d’ombre existe dans le modèle EF, mais n’existe pas sur le type .NET. EF effectue le suivi de la valeur de la propriété et de l’état en interne.
Les clés étrangères d’ombre sont généralement utilisées lorsqu’il existe un désir de masquer le concept relationnel d’une clé étrangère du modèle de domaine utilisé par le code d’application/la logique métier. Ce code d’application manipule ensuite entièrement la relation par le biais de navigations.
Conseil / Astuce
Si les entités vont être sérialisées, par exemple pour envoyer sur un câble, les valeurs de clé étrangère peuvent être un moyen utile de conserver les informations de relation intactes lorsque les entités ne sont pas dans un formulaire objet/graphique. Il est donc souvent pragmatique de conserver les propriétés de clé étrangère dans le type .NET à cet effet. Les propriétés de clé étrangère peuvent être privées, ce qui est souvent un bon compromis pour éviter d’exposer la clé étrangère tout en autorisant sa valeur à voyager avec l’entité.
Les propriétés de clé étrangère fantôme sont souvent créées par convention. Une clé étrangère fantôme sera également créée si l'argument de HasForeignKey
ne correspond à aucune propriété .NET. Par exemple:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("MyBlogId");
}
Par convention, une clé étrangère implicite obtient son type de la clé principale dans la relation. Ce type est rendu nullable, sauf si la relation est détectée ou configurée comme nécessaire.
La clé étrangère aléatoire peut également être définie explicitement, ce qui est utile pour configurer certains aspects de la propriété. Par exemple, pour rendre la propriété non nullable :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.Property<string>("MyBlogId")
.IsRequired();
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey("MyBlogId");
}
Conseil / Astuce
Par convention, les propriétés de clé étrangère héritent des facettes telles que la longueur maximale et la prise en charge Unicode de la clé principale dans la relation. Il est donc rarement nécessaire de configurer explicitement des facettes sur une propriété de clé étrangère.
La création d’une propriété fictive si le nom donné ne correspond à aucune propriété du type d’entité peut être désactivée à l’aide de ConfigureWarnings
. Par exemple:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ConfigureWarnings(b => b.Throw(CoreEventId.ShadowPropertyCreated));
Noms de contraintes de clé étrangère
Par convention, les contraintes de clé étrangère sont nommées FK_<dependent type name>_<principal type name>_<foreign key property name>
. Pour les clés étrangères composites, <foreign key property name>
devient une liste séparée par un trait de soulignement des noms de propriétés de clé étrangère.
Cela peut être modifié dans l’API de génération de modèles à l’aide HasConstraintName
de . Par exemple:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.HasConstraintName("My_BlogId_Constraint");
}
Conseil / Astuce
Le nom de contrainte n’est pas utilisé par le runtime EF. Il est utilisé uniquement lors de la création d’un schéma de base de données à l’aide d’EF Core Migrations.
Index des clés étrangères
Par convention, EF crée un index de base de données pour la propriété ou les propriétés d’une clé étrangère. Pour plus d’informations sur les types d’index créés par convention, consultez les conventions de génération de modèles.
Conseil / Astuce
Les relations sont définies dans le modèle EF entre les types d’entités inclus dans ce modèle. Certaines relations peuvent avoir besoin de référencer un type d’entité dans le modèle d’un autre contexte, par exemple lors de l’utilisation du modèle BoundedContext. Dans ce cas, la ou les colonnes de clé étrangère doivent être mappées aux propriétés normales, et ces propriétés peuvent ensuite être manipulées manuellement pour gérer les modifications apportées à la relation.
Clés principales
Par convention, les clés étrangères sont limitées à la clé primaire à la fin principale de la relation. Toutefois, une autre clé peut être utilisée à la place. Cette opération est obtenue à l’aide HasPrincipalKey
de l’API de génération de modèles. Par exemple, pour une clé étrangère pour une propriété unique :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => e.AlternateId);
}
Ou pour une clé étrangère composite avec plusieurs propriétés :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey(e => new { e.AlternateId1, e.AlternateId2 });
}
HasPrincipalKey
peut également se voir attribuer le nom de la propriété de clé alternative en tant que chaîne. Par exemple, pour une clé de propriété unique :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey("AlternateId");
}
Ou, pour une clé composite :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasPrincipalKey("AlternateId1", "AlternateId2");
}
Remarque
L’ordre des propriétés dans la clé principale et étrangère doit correspondre. Il s’agit également de l’ordre dans lequel la clé est définie dans le schéma de base de données. Il ne doit pas être identique à l’ordre des propriétés dans le type d’entité ou les colonnes de la table.
Il n’est pas nécessaire d’appeler HasAlternateKey
pour définir la clé secondaire sur l’entité principale. Cette opération est effectuée automatiquement lorsqu’elle HasPrincipalKey
est utilisée avec des propriétés qui ne sont pas les propriétés de clé primaire. Cependant, HasAlternateKey
peut être utilisé pour configurer davantage la clé alternative, comme pour définir son nom de contrainte de base de données. Pour plus d’informations, consultez la rubrique Clés.
Relations avec les entités sans clé
Chaque relation doit avoir une clé étrangère qui fait référence à une clé principale (primaire ou alternative). Cela signifie qu’un type d’entité sans clé ne peut pas agir comme l'élément principal d'une relation, car il n’existe aucune clé principale à laquelle les clés étrangères peuvent se référer.
Conseil / Astuce
Un type d’entité ne peut pas avoir une autre clé, mais aucune clé primaire. Dans ce cas, la clé secondaire (ou l’une des autres clés, s’il existe plusieurs) doit être promue vers la clé primaire.
Toutefois, les types d’entités sans clé peuvent toujours avoir des clés étrangères définies et peuvent donc servir de point de terminaison dépendant d'une relation. Par exemple, tenez compte de ces types, où Tag
n’a pas de clé :
public class Tag
{
public string Text { get; set; } = null!;
public int PostId { get; set; }
public Post Post { get; set; } = null!;
}
public class Post
{
public int Id { get; set; }
}
Tag
peut être configuré du côté dépendant de la relation :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.HasNoKey();
modelBuilder.Entity<Post>()
.HasMany<Tag>()
.WithOne(e => e.Post);
}
Remarque
EF ne prend pas en charge les navigations pointant vers des types d’entités sans clé. Consultez le problème GitHub #30331.
Clés étrangères dans les relations de plusieurs à plusieurs.
Dans les relations plusieurs-à-plusieurs, les clés étrangères sont définies sur le type d’entité de jointure et mappées aux contraintes de clé étrangère dans la table de jointure. Tout ce qui est décrit ci-dessus peut également être appliqué à ces clés d’entité de jointure étrangères. Par exemple, définition des noms de contrainte de base de données :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(e => e.Tags)
.WithMany(e => e.Posts)
.UsingEntity(
l => l.HasOne(typeof(Tag)).WithMany().HasConstraintName("TagForeignKey_Constraint"),
r => r.HasOne(typeof(Post)).WithMany().HasConstraintName("PostForeignKey_Constraint"));
}