Condividi tramite


Relazioni uno a uno

Le relazioni uno-a-uno vengono usate quando un'entità è associata con al più un'altra entità. Ad esempio, un Blog ha un BlogHeader, e quel BlogHeader appartiene a un singolo Blog.

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, una navigazione 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 chiavi esterne deve mappare sul 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à della chiave esterna BlogHeader.BlogId non può essere nulla. Ciò rende la relazione "obbligatoria" perché ogni dipendente () BlogHeader (Blog), perché la relativa proprietà di chiave esterna deve essere impostata su un valore.
  • Entrambe le entità hanno navigazioni che puntano all'entità correlata dall'altra parte della relazione.

Annotazioni

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 neanche un modo standard in un database relazionale, per assicurarsi che una parte principale sia associata a una parte dipendente. Se necessario, deve essere implementato nella logica dell'applicazione (business). Per altre informazioni, vedere Spostamenti necessari .

Suggerimento

Una relazione con due navigazioni, una da dipendente a principale e un inverso da principale a dipendente, è una relazione bidirezionale.

Questa relazione viene individuata per convenzione. Cioè:

  • Blog viene individuato come entità nella relazione e BlogHeader viene individuato come dipendente.
  • La chiave esterna della entità dipendente BlogHeader.BlogId viene individuata e fa riferimento alla chiave primaria Blog.Id dell'entità principale. La relazione viene individuata come necessario perché BlogHeader.BlogId non è annullabile.
  • Blog.BlogHeader viene scoperto come riferimento di navigazione.
  • BlogHeader.Blog viene scoperto come riferimento di navigazione.

Importante

Quando si usano tipi nullable di riferimento C#, la navigazione dall'entità dipendente al principale deve essere annullabile se la proprietà nullable della chiave esterna è annullabile. Se la proprietà della chiave esterna non è annullabile, l'associazione potrebbe essere annullabile o meno. In questo caso, BlogHeader.BlogId è non annullabile e BlogHeader.Blog è anche non annullabile. 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 ulteriori informazioni, consultare Uso dei tipi di 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. Per 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 in tutte le relazioni, iniziare con il tipo di entità dipendente (BlogHeader) è esattamente equivalente. Per 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, prima partendo dal principale e poi di nuovo partendo dal dipendente. Inoltre, il tentativo di configurare separatamente la parte principale e quella dipendente di una relazione generalmente 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 al principale sono ora annullabili. 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 annullabili C#, la proprietà di navigazione da dipendente a principale deve essere annullabile se la proprietà della chiave esterna è annullabile. In questo caso, BlogHeader.BlogId è nullable, quindi BlogHeader.Blog deve essere anche nullable. Per ulteriori informazioni, consultare Uso dei tipi di 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. Per 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. Questa operazione viene spesso definita relazione pk-to-PK. Ciò è possibile solo quando i tipi principale e dipendenti hanno gli stessi tipi di chiave primaria e la relazione risultante è sempre obbligatoria, poiché la chiave primaria del dipendente non può essere nullabile.

Qualsiasi relazione uno-a-uno in cui la chiave esterna non viene individuata per convenzione deve essere configurata per indicare l'entità principale e l'entità dipendente della relazione. Questa operazione viene in genere eseguita con una chiamata a HasForeignKey. Per 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. Per 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, il che spesso rappresenta un compromesso valido per evitare l'esposizione della chiave esterna, pur consentendo al suo valore di accompagnare l'entità.

A seguito dell'esempio precedente, in questo esempio viene rimossa l'attributo di 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 nullabili in C#, pertanto la nullabilità della navigazione da dipendente a principale viene utilizzata per determinare se la proprietà di chiave esterna è nullabile e quindi se la relazione è facoltativa o obbligatoria. Se i tipi riferimento nullable non vengono utilizzati, la proprietà ombra della chiave esterna sarà nullable per impostazione predefinita, rendendo così facoltativa la relazione. 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. Per esempio:

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

Facoltativa relazione uno-a-uno con chiave esterna fantasma

// 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 annullabile perché vengono usati tipi di riferimento nullable C# e la navigazione sul tipo di entità dipendente è annullabile. 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 i termini principale e dipendente.

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. Per 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 al principale

// 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 reintrodotta, ma la navigazione sull'entità dipendente è stata rimossa.

Suggerimento

Una relazione con una sola navigazione, da dipendente a principale o da principale 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. Per 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<>() . Per 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 navigazione verso il principale e con chiave esterna ombra

// 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 la navigazione sull'entità dipendente.

Come in precedenza, questa relazione richiede una configurazione per indicare i termini principale e dipendente.

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

Una configurazione più completa può essere utilizzata per configurare esplicitamente il nome della navigazione e della chiave esterna, con una chiamata appropriata a IsRequired() o IsRequired(false) secondo necessità. Per 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 navigazioni dal principale ai dipendenti, ma nessuna navigazione dal dipendente al principale. Per i due esempi successivi, la navigazione sul dipendente viene reintrodotta, mentre la navigazione sul principale viene rimossa.

Per convenzione, EF 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. Per 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<>() . Per 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 navigazioni

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 scoperta per convenzione, poiché non ci sono navigazioni che indicano che i due tipi sono correlati. Può essere configurato in modo esplicito in OnModelCreating. Per 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 annullabile.

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 sull'entità dipendente è vincolata alla proprietà della chiave primaria nell'entità principale. La chiave esterna può invece essere vincolata a una proprietà diversa, che diventa quindi una chiave alternativa per il tipo di entità principale. Per 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é EF creerà sempre, per convenzione, una relazione con la chiave primaria. Può essere configurato in modo esplicito in OnModelCreating usando una chiamata a HasPrincipalKey. Per 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. Per 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à. Per 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 scoperta per convenzione. Tuttavia, verrà individuato solo se la chiave composita è stata configurata in modo esplicito, poiché le chiavi composite non vengono individuate automaticamente. Per 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à. Per 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 l'entità dipendente non può esistere nel database una volta che l'entità principale è stata eliminata. 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);
}

Autoriferimento 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 sottostanti, ognuno Person è facoltativamente correlato a un altro 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
}

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. Per 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);
}

Annotazioni

Per le relazioni di auto-riferimento uno-a-uno, poiché i tipi di entità principale e dipendente sono gli stessi, specificare quale tipo contiene la chiave esterna non chiarisce l'estremità dipendente. In questo caso, la navigazione specificata in HasOne punta da dipendente a principale, e la navigazione specificata in WithOne punta da principale a dipendente.