Relazioni uno a uno

Le relazioni uno-a-uno vengono usate quando un'entità è associata al massimo un'altra entità. Ad esempio, un oggetto Blog ha un BlogHeaderoggetto e che BlogHeader appartiene a un singolo Blogoggetto .

Questo documento è strutturato in molti esempi. Gli esempi iniziano con i casi comuni, che introducono anche concetti. Negli esempi successivi vengono illustrati i tipi di configurazione meno comuni. Un buon approccio consiste nel comprendere i primi esempi e i concetti, quindi passare agli esempi successivi in base alle esigenze specifiche. In base a questo approccio, si inizierà con semplici relazioni "obbligatorie" e "facoltative" uno-a-uno.

Suggerimento

Il codice per tutti gli esempi seguenti è disponibile in OneToOne.cs.

Obbligatorio uno-a-uno

// 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
}

Una relazione uno-a-uno è costituita da:

  • Una o più proprietà chiave primaria o alternativa nell'entità principale. Ad esempio: Blog.Id.
  • Una o più proprietà di chiave esterna nell'entità dipendente. Ad esempio: BlogHeader.BlogId.
  • Facoltativamente, una navigazione di riferimento sull'entità principale che fa riferimento all'entità dipendente. Ad esempio: Blog.Header.
  • Facoltativamente, uno spostamento di riferimento sull'entità dipendente che fa riferimento all'entità principale. Ad esempio: BlogHeader.Blog.

Suggerimento

Non è sempre ovvio quale lato di una relazione uno-a-uno deve essere l'entità principale e quale lato deve essere il dipendente. Ecco alcune considerazioni:

  • Se le tabelle di database per i due tipi esistono già, la tabella con le colonne chiave esterna deve eseguire il mapping al tipo dipendente.
  • Un tipo è in genere il tipo dipendente se non può esistere logicamente senza l'altro tipo. Ad esempio, non ha senso avere un'intestazione per un blog che non esiste, quindi BlogHeader è naturalmente il tipo dipendente.
  • Se esiste una relazione padre/figlio naturale, il figlio è in genere il tipo dipendente.

Quindi, per la relazione in questo esempio:

  • La proprietà BlogHeader.BlogId della chiave esterna non è nullable. Ciò rende la relazione "obbligatoria" perché ogni dipendente () deve essere correlato a un'entità (BlogHeaderBlog), perché la relativa proprietà di chiave esterna deve essere impostata su un valore.
  • Entrambe le entità hanno spostamenti che puntano all'entità correlata dall'altra parte della relazione.

Nota

Una relazione obbligatoria garantisce che ogni entità dipendente debba essere associata a un'entità principale. Tuttavia, un'entità principale può esistere sempre senza alcuna entità dipendente. Ovvero, una relazione obbligatoria non indica che sarà sempre presente un'entità dipendente. Non esiste alcun modo nel modello di Entity Framework e non esiste un modo standard in un database relazionale, per assicurarsi che un'entità sia associata a un dipendente. Se necessario, deve essere implementato nella logica dell'applicazione (business). Per altre informazioni, vedere Spostamenti necessari.

Suggerimento

Una relazione con due spostamenti, uno da dipendente a principal e un inverso da entità a dipendente, è noto come relazione bidirezionale.

Questa relazione viene individuata per convenzione. Ovvero:

  • Blog viene individuato come entità nella relazione e BlogHeader viene individuato come dipendente.
  • BlogHeader.BlogId viene individuato come chiave esterna del dipendente che fa riferimento alla Blog.Id chiave primaria dell'entità. La relazione viene individuata come richiesto perché BlogHeader.BlogId non è nullable.
  • Blog.BlogHeader viene individuato come navigazione di riferimento.
  • BlogHeader.Blog viene individuato come navigazione di riferimento.

Importante

Quando si usano tipi di riferimento nullable C#, la navigazione dall'entità dipendente all'entità deve essere nullable se la proprietà della chiave esterna è nullable. Se la proprietà di chiave esterna non è nullable, lo spostamento potrebbe essere nullable o no. In questo caso, BlogHeader.BlogId è non nullable ed BlogHeader.Blog è anche non nullable. Il = null!; costrutto viene usato per contrassegnarlo come intenzionale per il compilatore C#, poiché EF imposta in genere l'istanza Blog e non può essere Null per una relazione completamente caricata. Per altre informazioni, vedere Uso dei tipi riferimento nullable.

Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Nell'esempio precedente, la configurazione delle relazioni avvia il tipo di entità principale (Blog). Come per tutte le relazioni, è esattamente equivalente a iniziare con il tipo di entità dipendente (BlogHeader). Ad esempio:

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

Nessuna di queste opzioni è migliore dell'altra; entrambi comportano esattamente la stessa configurazione.

Suggerimento

Non è mai necessario configurare una relazione due volte, una volta a partire dall'entità e quindi ricominciare dall'oggetto dipendente. Inoltre, il tentativo di configurare l'entità di sicurezza e le metà dipendenti di una relazione separatamente in genere non funziona. Scegliere di configurare ogni relazione da un'estremità o dall'altra e quindi scrivere il codice di configurazione una sola volta.

Facoltativo uno-a-uno

// 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
}

Si tratta dello stesso esempio precedente, ad eccezione del fatto che la proprietà della chiave esterna e la navigazione all'entità sono ora nullable. Ciò rende la relazione "facoltativa" perché un dipendente (BlogHeader) non può essere correlato ad alcuna entità (Blog) impostando la relativa proprietà di chiave esterna e lo spostamento su null.

Importante

Quando si usano tipi di riferimento nullable C#, la proprietà di navigazione da dipendente a principal deve essere nullable se la proprietà della chiave esterna è nullable. In questo caso, BlogHeader.BlogId è nullable, quindi BlogHeader.Blog deve essere anche nullable. Per altre informazioni, vedere Uso dei tipi riferimento nullable.

Come in precedenza, questa relazione viene individuata per convenzione. Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Relazione da uno a uno con chiave primaria a chiave primaria

// 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
}

A differenza delle relazioni uno-a-molti, la fine dipendente di una relazione uno-a-uno può usare la proprietà o le proprietà della chiave primaria come proprietà o proprietà della chiave esterna. Si tratta spesso di una relazione PK-to-PK. Ciò è possibile solo quando i tipi principal e dipendenti hanno gli stessi tipi di chiave primaria e la relazione risultante è sempre necessaria, poiché la chiave primaria del dipendente non può essere nullable.

Qualsiasi relazione uno-a-uno in cui la chiave esterna non viene individuata per convenzione deve essere configurata per indicare l'entità di sicurezza e le estremità dipendenti della relazione. Questa operazione viene in genere eseguita con una chiamata a HasForeignKey. Ad esempio:

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

Suggerimento

HasPrincipalKey può anche essere usato per questo scopo, ma in questo caso è meno comune.

Quando non viene specificata alcuna proprietà nella chiamata a HasForeignKeye la chiave primaria è adatta, viene usata come chiave esterna. Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Obbligatorio uno-a-uno con chiave esterna ombreggiatura

// 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
}

In alcuni casi, potrebbe non essere necessaria una proprietà di chiave esterna nel modello, poiché le chiavi esterne sono un dettaglio del modo in cui la relazione viene rappresentata nel database, che non è necessaria quando si usa la relazione in modo puramente orientato agli oggetti. Tuttavia, se le entità verranno serializzate, ad esempio per inviare in rete, i valori di chiave esterna possono essere un modo utile per mantenere intatte le informazioni sulla relazione quando le entità non si trovano in un modulo oggetto. È quindi spesso pragmatico mantenere le proprietà di chiave esterna nel tipo .NET a questo scopo. Le proprietà della chiave esterna possono essere private, che spesso rappresenta un buon compromesso per evitare di esporre la chiave esterna, consentendo al contempo il relativo valore di spostarsi con l'entità.

Seguendo l'esempio precedente, in questo esempio viene rimossa la proprietà della chiave esterna dal tipo di entità dipendente. Tuttavia, anziché usare la chiave primaria, EF viene invece indicato di creare una proprietà di chiave esterna shadow denominata BlogId di tipo int.

Un punto importante da notare è che vengono usati tipi di riferimento nullable in C#, pertanto viene usato il supporto dei valori Null della struttura di spostamento da dipendente a principal per determinare se la proprietà di chiave esterna è nullable e quindi se la relazione è facoltativa o obbligatoria. Se i tipi riferimento nullable non vengono utilizzati, la proprietà della chiave esterna shadow sarà nullable per impostazione predefinita, rendendo la relazione facoltativa per impostazione predefinita. In questo caso, usare IsRequired per forzare la proprietà della chiave esterna shadow in modo che non sia nullable e rendere necessaria la relazione.

Questa relazione richiede di nuovo una configurazione per indicare le estremità principali e dipendenti:

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

Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Facoltativo uno-a-uno con chiave esterna ombreggiatura

// 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
}

Come nell'esempio precedente, la proprietà di chiave esterna è stata rimossa dal tipo di entità dipendente. Tuttavia, a differenza dell'esempio precedente, questa volta la proprietà della chiave esterna viene creata come nullable perché vengono usati tipi di riferimento nullable C# e lo spostamento sul tipo di entità dipendente è nullable. In questo modo la relazione è facoltativa.

Quando i tipi di riferimento nullable C# non vengono usati, la proprietà della chiave esterna verrà creata come nullable per impostazione predefinita. Ciò significa che le relazioni con le proprietà shadow create automaticamente sono facoltative per impostazione predefinita.

Come in precedenza, questa relazione richiede una configurazione per indicare l'entità di sicurezza e le estremità dipendenti:

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

Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Uno-a-uno senza navigazione all'entità di sicurezza

// 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
}

Per questo esempio, la proprietà di chiave esterna è stata nuovamente introdotta, ma la navigazione sul dipendente è stata rimossa.

Suggerimento

Una relazione con una sola struttura di spostamento, da dipendente a principal o da entità a dipendente, ma non entrambe, è nota come relazione unidirezionale.

Questa relazione viene individuata per convenzione, poiché viene individuata la chiave esterna, indicando così il lato dipendente. Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Si noti che la chiamata a WithOne non ha argomenti. Questo è il modo per indicare a ENTITY Framework che non è presente alcuna navigazione da BlogHeader a Blog.

Se la configurazione inizia dall'entità senza navigazione, il tipo dell'entità sull'altra estremità della relazione deve essere specificato in modo esplicito usando la chiamata generica HasOne<>() . Ad esempio:

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

Uno-a-uno senza spostamento all'entità di sicurezza e con chiave esterna ombreggiatura

// 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; }
}

In questo esempio vengono combinati due degli esempi precedenti rimuovendo sia la proprietà di chiave esterna che lo spostamento sul dipendente.

Come in precedenza, questa relazione richiede una configurazione per indicare l'entità di sicurezza e le estremità dipendenti:

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

Una configurazione più completa può essere usata per configurare in modo esplicito il nome della chiave esterna e di navigazione, con una chiamata appropriata a o IsRequired(false) in base alle IsRequired() esigenze. Ad esempio:

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

Uno-a-uno senza spostamento a dipendente

// 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
}

I due esempi precedenti avevano spostamenti dall'entità ai dipendenti, ma nessuna navigazione dall'entità dipendente all'entità. Per i due esempi successivi, lo spostamento sul dipendente viene introdotto nuovamente, mentre lo spostamento sull'entità viene rimosso.

Per convenzione, Entity Framework considererà questa relazione come una relazione uno-a-molti. Per renderla uno-a-uno è necessaria una configurazione minima:

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

Si noti di nuovo che WithOne() viene chiamato senza argomenti per indicare che non è presente alcuna navigazione in questa direzione.

Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Se la configurazione inizia dall'entità senza navigazione, il tipo dell'entità sull'altra estremità della relazione deve essere specificato in modo esplicito usando la chiamata generica HasOne<>() . Ad esempio:

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

Uno-a-uno senza spostamenti

In alcuni casi, può essere utile configurare una relazione senza spostamenti. Tale relazione può essere modificata solo modificando direttamente il valore della chiave esterna.

// 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
}

Questa relazione non viene individuata per convenzione, poiché non sono presenti spostamenti che indicano che i due tipi sono correlati. Può essere configurato in modo esplicito in OnModelCreating. Ad esempio:

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

Con questa configurazione, la BlogHeader.BlogId proprietà viene ancora rilevata come chiave esterna per convenzione e la relazione è "obbligatoria" perché la proprietà della chiave esterna non è nullable. La relazione può essere resa "facoltativa" rendendo la proprietà di chiave esterna nullable.

Una configurazione esplicita più completa di questa relazione è:

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

Uno-a-uno con chiave alternativa

In tutti gli esempi finora, la proprietà di chiave esterna sul dipendente è vincolata alla proprietà della chiave primaria nell'entità. La chiave esterna può invece essere vincolata a una proprietà diversa, che diventa quindi una chiave alternativa per il tipo di entità principale. Ad esempio:

// 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
}

Questa relazione non viene individuata per convenzione, poiché ENTITY creerà sempre, per convenzione, una relazione con la chiave primaria. Può essere configurato in modo esplicito usando OnModelCreating una chiamata a HasPrincipalKey. Ad esempio:

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

HasPrincipalKey può essere combinato con altre chiamate per configurare in modo esplicito gli spostamenti, le proprietà della chiave esterna e la natura obbligatoria/facoltativa. Ad esempio:

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();
}

Uno-a-uno con chiave esterna composita

In tutti gli esempi finora, la proprietà chiave primaria o alternativa dell'entità è costituita da una singola proprietà. Le chiavi primarie o alternative possono anche essere formate da più di una proprietà. Queste chiavi sono note come "chiavi composite". Quando l'entità di una relazione ha una chiave composta, la chiave esterna del dipendente deve essere anche una chiave composta con lo stesso numero di proprietà. Ad esempio:

// 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
}

Questa relazione viene individuata per convenzione. Tuttavia, verrà individuato solo se la chiave composita è stata configurata in modo esplicito, poiché le chiavi composite non vengono individuate automaticamente. Ad esempio:

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

Importante

Un valore di chiave esterna composita viene considerato null se uno dei relativi valori di proprietà è Null. Una chiave esterna composita con una proprietà Null e un'altra non Null non verrà considerata una corrispondenza per una chiave primaria o alternativa con gli stessi valori. Entrambi verranno considerati null.

Sia HasForeignKey che HasPrincipalKey possono essere usati per specificare in modo esplicito le chiavi con più proprietà. Ad esempio:

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();
        });
}

Suggerimento

Nel codice precedente, le chiamate a HasKey e HasOne sono state raggruppate in un generatore annidato. I generatori annidati eliminano la necessità di chiamare Entity<>() più volte per lo stesso tipo di entità, ma sono funzionalmente equivalenti alla chiamata Entity<>() più volte.

Obbligatorio uno-a-uno senza eliminazione a catena

// 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
}

Per convenzione, le relazioni necessarie sono configurate per l'eliminazione a catena. Ciò è dovuto al fatto che il dipendente non può esistere nel database dopo l'eliminazione dell'entità. Il database può essere configurato per generare un errore, in genere arrestando in modo anomalo l'applicazione, invece di eliminare automaticamente le righe dipendenti che non possono più esistere. Questa operazione richiede alcune configurazioni:

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

Self-referencing uno-a-uno

In tutti gli esempi precedenti il tipo di entità principale è diverso dal tipo di entità dipendente. Questo non deve essere il caso. Ad esempio, nei tipi seguenti, ognuno Person è facoltativamente correlato a un altro Personoggetto .

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
}

Questa relazione viene individuata per convenzione. Nei casi in cui gli spostamenti, la chiave esterna o la natura obbligatoria/facoltativa della relazione non vengono individuati per convenzione, questi elementi possono essere configurati in modo esplicito. Ad esempio:

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

Nota

Per le relazioni di riferimento uno-a-uno, poiché i tipi di entità principale e dipendente sono gli stessi, specificando quale tipo contiene la chiave esterna non chiarisce la fine dipendente. In questo caso, lo spostamento specificato in HasOne punti da dipendente a entità e lo spostamento specificato in WithOne punti da entità a dipendente.