Relations un à un

Les relations un à un sont utilisées lorsqu'une entité est associée à une autre entité au maximum. Par exemple, un Blog a un BlogHeaderet que BlogHeader appartient à un seul Blog.

Ce document est structuré autour de nombreux exemples. Les exemples commencent par des cas courants, qui introduisent également des concepts. Les exemples ultérieurs couvrent des types de configuration moins courants. Une bonne approche ici consiste à comprendre les premiers exemples et concepts, puis à accéder aux exemples ultérieurs en fonction de vos besoins spécifiques. En fonction de cette approche, nous allons commencer par des relations un-à-un « obligatoire » et « facultatif » simples.

Conseil

Le code de tous les exemples ci-dessous est disponible dans OneToMany.cs.

Un-à-un obligatoire

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Une relation un-à-un est constituée des éléments suivants :

  • Une ou plusieurs propriétés clé primaire ou alternative sur l’entité principale. Par exemple, Blog.Id
  • Une ou plusieurs propriétés clé étrangère sur l’entité dépendante. Par exemple, BlogHeader.BlogId
  • Si vous le souhaitez, une navigation de référence sur l’entité principale référençant l’entité dépendante. Par exemple, Blog.Header
  • Si vous le souhaitez, une navigation de référence sur l’entité dépendante référençant l’entité principale. Par exemple, BlogHeader.Blog

Conseil

Il n’est pas toujours évident quel membre d’une relation un-à-un doit être l’entité principale, et quel côté doit être l’entité dépendante. Les autres éléments à prendre en compte sont les suivants :

  • Si les tables de base de données pour les deux types existent déjà, la table avec les colonnes de clé étrangère doit être mappée au type dépendant.
  • Un type est généralement le type dépendant s’il ne peut pas exister logiquement sans l’autre type. Par exemple, il n’est pas judicieux d’avoir un en-tête pour un blog qui n’existe pas, donc BlogHeader est naturellement le type dépendant.
  • S’il existe une relation parent/enfant naturelle, l’enfant est généralement le type dépendant.

Par conséquent, pour la relation dans cet exemple :

  • La propriété de clé étrangère BlogHeader.BlogId n’est pas nullable. Cela rend la relation « obligatoire », car chaque entité dépendante (BlogHeader) doit être liée à une certaine entité principale (Blog), car sa propriété de clé étrangère doit être définie sur une valeur.
  • Les deux entités ont des navigations pointant vers l’entité ou les entités associées de l’autre côté de la relation.

Remarque

Une relation obligatoire garantit que chaque entité dépendante doit être associée à une entité principale. Toutefois, une entité principale peut toujours exister sans entité dépendante. Autrement dit, une relation obligatoire n'indique pas qu’il y aura toujours au moins une entité dépendante. Il n’existe aucun moyen dans le modèle EF, et pas non plus de façon standard dans une base de données relationnelle, pour s’assurer qu’une entité principale est associée à un certain nombre d’entités dépendantes. Si nécessaire, cela doit être implémenté dans la logique (métier) d’application. Pour plus d’informations, consultez Navigations obligatoires.

Conseil

Une relation avec deux navigations, une d’entité dépendante à l’entité principale et une inverse d’entité principale aux entités dépendantes, est connue sous le nom de relation bidirectionnelle.

Cette relation est découverte par convention. Plus précisément :

  • Blog est découvert comme entité principale dans la relation et BlogHeader est découvert comme entité dépendante.
  • BlogHeader.BlogId est découvert en tant que clé étrangère de la clé dépendante référençant la clé primaire Blog.Id de l’entité principale. La relation est détectée comme obligatoire, car BlogHeader.BlogId n’est pas nullable.
  • Blog.BlogHeader est découvert comme navigation de référence.
  • BlogHeader.Blog est découvert comme navigation de référence.

Important

Lorsque vous utilisez les types de référence C# nullables, la navigation de référence doit être nullable si la propriété de clé étrangère est nullable. Si la propriété de clé étrangère n’est pas nullable, la navigation de référence peut être nullable ou non. Dans ce cas, BlogHeader.BlogId est non nullable et BlogHeader.Blog est également non nullable. La construction = null!; est utilisée pour marquer cela comme intentionnel pour le compilateur C#, car EF définit généralement l’instance Blog et ne peut pas être null pour une relation entièrement chargée. Pour plus d’informations, consultez Utilisation des types de référence Nullable .

Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Dans l’exemple ci-dessus, la configuration des relations commence par le type d’entité principale (Blog). Comme pour toutes les relations, cela revient exactement à commencer plutôt par le type d’entité dépendante (BlogHeader). Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne(e => e.Header)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Aucune de ces options n’est meilleure que l’autre, elles entraînent toutes deux exactement la même configuration.

Conseil

Il n’est jamais nécessaire de configurer une relation deux fois, une fois à partir de l’entité principale, puis à nouveau à partir de l’entité dépendante. En outre, la tentative de configurer l’entité principale et les moitiés dépendantes d’une relation séparément ne fonctionnent généralement pas. Choisissez de configurer chaque relation d’une extrémité ou de l’autre, puis d’écrire le code de configuration une seule fois.

Un-à-un facultatif

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int? BlogId { get; set; } // Optional foreign key property
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Il s’agit de la même chose que l’exemple précédent, sauf que la propriété de clé étrangère et la navigation vers l’entité principale sont désormais nullables. Cela rend la relation « facultative », car une dépendance (BlogHeader) ne peut pas être associée à n’importe quel principal (Blog) en définissant sa propriété de clé étrangère et sa navigation sur null.

Important

Lorsque vous utilisez les types de référence C# nullables, la propriété de navigation du dépendant au principal doit être nullable si la propriété de clé étrangère est nullable. Dans ce cas, BlogHeader.BlogId est nullable. Par conséquent, BlogHeader.Blog doit également être nullable. Pour plus d’informations, consultez Utilisation des types de référence Nullable .

Comme précédemment, cette relation est découverte par convention. Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired(false);
}

Un-à-un obligatoire avec la relation de clé primaire à clé primaire

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Contrairement aux relations un-à-plusieurs, la fin dépendante d’une relation un-à-un peut utiliser sa propriété de clé primaire ou ses propriétés comme propriété ou propriétés de clé étrangère. Il s’agit souvent d’une relation PK-à-PK. Cela n’est possible que lorsque les types principaux et dépendants ont les mêmes types de clés primaires, et que la relation résultante est toujours obligatoire, car la clé primaire du dépendant ne peut pas être nullable.

Toute relation un-à-un où la clé étrangère n’est pas découverte par convention doit être configurée pour indiquer les extrémités principal et dépendant de la relation. Cela se fait généralement par un appel à HasForeignKey. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>();
}

Conseil

HasPrincipalKey peut également être utilisé à cet effet, mais cela est moins courant.

Quand aucune propriété n’est spécifiée dans l’appel à HasForeignKey, et que la clé primaire convient, elle est utilisée comme clé étrangère. Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.Id)
        .IsRequired();
}

Un-à-un obligatoire avec une clé étrangère cachée

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Dans certains cas, vous ne souhaiterez peut-être pas une propriété de clé étrangère dans votre modèle, car les clés étrangères sont un détail de la façon dont la relation est représentée dans la base de données, ce qui n’est pas nécessaire lors de l’utilisation de la relation de manière purement orientée objet. Toutefois, si les entités doivent être sérialisées, par exemple pour être envoyées 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 sous forme d’objets. 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é.

À partir de l’exemple précédent, cet exemple supprime la propriété de clé étrangère du type d’entité dépendant. Toutefois, au lieu d’utiliser la clé primaire, EF est plutôt invité à créer une propriété de clé étrangère cachée appelée BlogId de type int.

Un point important à noter ici est que les types de référence nullables C# sont utilisés, de sorte que la possibilité null de la navigation de du dépendant au principal est utilisée pour déterminer si la propriété de clé étrangère est nullable ou non, et par conséquent, si la relation est facultative ou obligatoire. Si les types de référence nullables ne sont pas utilisés, la propriété de clé étrangère cachée sera nullable par défaut, ce qui rend la relation facultative par défaut. Dans ce cas, utilisez IsRequired pour forcer la propriété de clé étrangère cachée à être non nullable et rendre la relation obligatoire.

Cette relation a à nouveau besoin d’une configuration pour indiquer les extrémités principal et dépendant :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Un-à-un facultatif avec une clé étrangère cachée

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Comme dans l’exemple précédent, la propriété de clé étrangère a été supprimée du type d’entité dépendant. Contrairement à l’exemple précédent, cette fois,la propriété de clé étrangère est créée comme nullable, car les types de référence nullables C# sont utilisés et la navigation sur le type d’entité dépendant est nullable. Cela rend la relation facultative.

Lorsque les types de référence nullables C# ne sont pas utilisés, la propriété de clé étrangère est également créée comme nullable par défaut. Cela signifie que les relations avec les propriétés cachées créées automatiquement sont facultatives par défaut.

Comme précédemment, cette relation a besoin d’une configuration pour indiquer les extrémités principal et dépendant :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired(false);
}

Un-à-plusieurs sans navigation vers le principal

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Pour cet exemple, la propriété de clé étrangère a été réintroduite, mais la navigation sur le dépendant a été supprimée.

Conseil

Une relation avec une seule navigation, une de dépendant au principal ou d’un principal au dépendant, mais pas les deux, est connue sous le nom de relation unidirectionnelle.

Cette relation est découverte par la convention, car la clé étrangère est découverte, indiquant ainsi le côté dépendant. Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Notez que l’appel à WithOne n’a aucun argument. C’est la façon de dire à EF qu’il n’y a pas de navigation de BlogHeader à Blog.

Si la configuration commence à partir de l’entité sans navigation, le type de l’entité à l’autre extrémité de la relation doit être spécifié explicitement à l’aide de l’appel générique HasOne<>(). Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne<Blog>()
        .WithOne(e => e.Header)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Un-à-un sans navigation vers principal et avec une clé étrangère cachée

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
}

Cet exemple combine deux des exemples précédents en supprimant à la fois la propriété de clé étrangère et la navigation sur le dépendant.

Comme précédemment, cette relation a besoin d’une configuration pour indiquer les extrémités principal et dépendant :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Une configuration plus complète peut être utilisée pour configurer explicitement le nom de la navigation et de la clé étrangère, avec un appel approprié à IsRequired() ou IsRequired(false) si nécessaire. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Un-à-un sans navigation vers le dépendant

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Les deux exemples précédents comportaient des navigations entre le principal et les dépendants, mais aucune navigation entre le dépendant et le principal. Pour les deux exemples suivants, la navigation sur la dépendance est réintroduite, tandis que la navigation sur le principal est supprimée à la place.

Par convention, EF traitera cela comme une relation un-à-plusieurs. Une configuration minimale est nécessaire pour la rendre un-à-un :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne();
}

Notez à nouveau que WithOne() est appelé sans arguments pour indiquer qu’il n’y a pas de navigation dans cette direction.

Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Si la configuration commence à partir de l’entité sans navigation, le type de l’entité à l’autre extrémité de la relation doit être spécifié explicitement à l’aide de l’appel générique HasOne<>(). Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Un-à-plusieurs sans navigation

Parfois, il peut être utile de configurer une relation sans navigation. Une telle relation ne peut être manipulée qu’en modifiant directement la valeur de clé étrangère.

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Cette relation n’est pas découverte par convention, car il n’existe aucune navigation indiquant que les deux types sont liés. Elle peut être configurée explicitement dans OnModelCreating. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne();
}

Avec cette configuration, la propriété BlogHeader.BlogId est toujours détectée comme clé étrangère par convention et la relation est obligatoire, car la propriété de clé étrangère n’est pas nullable. La relation peut être rendue « facultative » en rendant la propriété de clé étrangère nullable.

Une configuration explicite plus complète de cette relation est la suivante :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Un-à-plusieurs avec une autre clé

Dans tous les exemples jusqu’à présent, la propriété de clé étrangère sur la dépendance est limitée à la propriété de clé primaire sur le principal. La clé étrangère peut à la place être contrainte à une propriété différente, qui devient ensuite une autre clé pour le type d’entité principal. Par exemple :

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public int AlternateId { get; set; } // Alternate key as target of the BlogHeader.BlogId foreign key
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Cette relation n’est pas découverte par convention, car EF crée toujours, par convention, une relation avec la clé primaire. Elle peut être configurée explicitement dans OnModelCreating à l’aide d’un appel à HasPrincipalKey. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasPrincipalKey<Blog>(e => e.AlternateId);
}

HasPrincipalKey peut être combiné avec d’autres appels pour configurer explicitement les navigations, les propriétés de clé étrangère et la nature obligatoire/facultative. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasPrincipalKey<Blog>(e => e.AlternateId)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Un-à-un avec clé étrangère composite

Dans tous les exemples jusqu’à présent, la propriété de clé primaire ou alternative du principal était constituée d’une propriété unique. Les clés primaire ou alternative peuvent également être formées sous la forme de plusieurs propriétés : elles sont appelées « clés composites ». Lorsque le principal d’une relation a une clé composite, la clé étrangère du dépendant doit également être une clé composite avec le même nombre de propriétés. Par exemple :

// Principal (parent)
public class Blog
{
    public int Id1 { get; set; } // Composite key part 1
    public int Id2 { get; set; } // Composite key part 2
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId1 { get; set; } // Required foreign key property part 1
    public int BlogId2 { get; set; } // Required foreign key property part 2
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Cette relation est découverte par convention. Toutefois, elle ne sera détectée que si la clé composite a été configurée explicitement, car les clés composites ne sont pas découvertes automatiquement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasKey(e => new { e.Id1, e.Id2 });
}

Important

Une valeur de clé étrangère composite est considérée comme null si l’une de ses valeurs de propriété est null. Une clé étrangère composite avec une propriété null et une autre valeur non null ne sera pas considérée comme une correspondance pour une clé primaire ou alternative avec les mêmes valeurs. Les deux seront considérées comme null.

HasForeignKey et HasPrincipalKey peuvent être utilisés pour spécifier explicitement des clés avec plusieurs propriétés. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        nestedBuilder =>
        {
            nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });

            nestedBuilder.HasOne(e => e.Header)
                .WithOne(e => e.Blog)
                .HasPrincipalKey<Blog>(e => new { e.Id1, e.Id2 })
                .HasForeignKey<BlogHeader>(e => new { e.BlogId1, e.BlogId2 })
                .IsRequired();
        });
}

Conseil

Dans le code ci-dessus, les appels à HasKey et HasOne ont été regroupés dans un générateur imbriqué. Les générateurs imbriqués suppriment la nécessité d’appeler Entity<>() plusieurs fois pour le même type d’entité, mais sont fonctionnellement équivalents à la répétition de l’appel Entity<>().

Un-à-un obligatoire sans suppression en cascade

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Par convention, les relations obligatoires sont configurées pour utiliser les suppressions en cascade. Cela est dû au fait que le dépendant ne peut pas exister dans la base de données une fois que le principal a été supprimé. La base de données peut être configurée pour générer une erreur, généralement en plantant l’application, au lieu de supprimer automatiquement des lignes dépendantes qui ne peuvent plus exister. Cela nécessite une configuration.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .OnDelete(DeleteBehavior.Restrict);
}

Un-à-un de référencement automatique

Dans tous les exemples précédents, le type d’entité principal était différent du type d’entité dépendant. Ce n'est pas forcément le cas. Par exemple, dans les types ci-dessous, chaque Person est facultativement liée à un autre Person.

public class Person
{
    public int Id { get; set; }

    public int? HusbandId { get; set; } // Optional foreign key property
    public Person? Husband { get; set; } // Optional reference navigation to principal
    public Person? Wife { get; set; } // Reference navigation to dependent
}

Cette relation est découverte par convention. Dans les cas où les navigations, la clé étrangère ou la nature obligatoire/facultative de la relation ne sont pas découvertes par convention, ces éléments peuvent être configurés explicitement. Par exemple :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOne(e => e.Husband)
        .WithOne(e => e.Wife)
        .HasForeignKey<Person>(e => e.HusbandId)
        .IsRequired(false);
}

Remarque

Pour les relations de référencement automatique un-à-un, étant donné que les types d’entité principal et dépendant sont identiques, la spécification du type contenant la clé étrangère ne clarifie pas l’extrémité dépendante. Dans ce cas, la navigation spécifiée dans HasOne pointe du dépendant au principal et la navigation spécifiée dans WithOne pointe du principal au dépendant.