RelazioniRelationships

Una relazione definisce il modo in cui due entità sono correlate tra loro.A relationship defines how two entities relate to each other. In un database relazionale, questo è rappresentato da un vincolo FOREIGN KEY.In a relational database, this is represented by a foreign key constraint.

Nota

La maggior parte degli esempi in questo articolo usa una relazione uno-a-molti per illustrare i concetti.Most of the samples in this article use a one-to-many relationship to demonstrate concepts. Per esempi di relazioni uno-a-uno e molti-a-molti, vedere la sezione altri modelli di relazione alla fine dell'articolo.For examples of one-to-one and many-to-many relationships see the Other Relationship Patterns section at the end of the article.

Definizione dei terminiDefinition of Terms

Esistono diversi termini usati per descrivere le relazioniThere are a number of terms used to describe relationships

  • Entità dipendente: Si tratta dell'entità che contiene le proprietà di chiave esterna.Dependent entity: This is the entity that contains the foreign key property(s). Noto anche come ' Child ' della relazione.Sometimes referred to as the 'child' of the relationship.

  • Entità principale: Si tratta dell'entità che contiene le proprietà della chiave primaria/alternativa.Principal entity: This is the entity that contains the primary/alternate key property(s). Noto anche come ' Parent ' della relazione.Sometimes referred to as the 'parent' of the relationship.

  • Chiave esterna: Proprietà nell'entità dipendente utilizzata per archiviare i valori della proprietà della chiave principale a cui è correlata l'entità.Foreign key: The property(s) in the dependent entity that is used to store the values of the principal key property that the entity is related to.

  • Chiave principale: Proprietà che identifica in modo univoco l'entità principale.Principal key: The property(s) that uniquely identifies the principal entity. Può trattarsi della chiave primaria o di una chiave alternativa.This may be the primary key or an alternate key.

  • Proprietà di navigazione: Proprietà definita nell'entità dipendente e/o dipendente che contiene uno o più riferimenti alle entità correlate.Navigation property: A property defined on the principal and/or dependent entity that contains a reference(s) to the related entity(s).

    • Proprietà di navigazione della raccolta: Proprietà di navigazione che contiene riferimenti a molte entità correlate.Collection navigation property: A navigation property that contains references to many related entities.

    • Proprietà di navigazione riferimento: Proprietà di navigazione che include un riferimento a una singola entità correlata.Reference navigation property: A navigation property that holds a reference to a single related entity.

    • Proprietà di navigazione inversa: Quando si discute una particolare proprietà di navigazione, questo termine fa riferimento alla proprietà di navigazione nell'altra entità finale della relazione.Inverse navigation property: When discussing a particular navigation property, this term refers to the navigation property on the other end of the relationship.

Nel listato di codice seguente viene illustrata una relazione uno-a-molti tra Blog e PostThe following code listing shows a one-to-many relationship between Blog and Post

  • Post è l'entità dipendentePost is the dependent entity

  • Blog è l'entità principaleBlog is the principal entity

  • Post.BlogId è la chiave esternaPost.BlogId is the foreign key

  • Blog.BlogId è la chiave principale (in questo caso è una chiave primaria anziché una chiave alternativa)Blog.BlogId is the principal key (in this case it is a primary key rather than an alternate key)

  • Post.Blog è una proprietà di navigazione di riferimentoPost.Blog is a reference navigation property

  • Blog.Posts è una proprietà di navigazione della raccoltaBlog.Posts is a collection navigation property

  • Post.Blog è la proprietà di navigazione inversa di Blog.Posts (e viceversa)Post.Blog is the inverse navigation property of Blog.Posts (and vice versa)

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

ConvenzioniConventions

Per convenzione, viene creata una relazione quando viene individuata una proprietà di navigazione su un tipo.By convention, a relationship will be created when there is a navigation property discovered on a type. Una proprietà viene considerata una proprietà di navigazione se il tipo a cui punta non può essere mappato come tipo scalare dal provider di database corrente.A property is considered a navigation property if the type it points to can not be mapped as a scalar type by the current database provider.

Nota

Le relazioni individuate per convenzione saranno sempre destinate alla chiave primaria dell'entità principale.Relationships that are discovered by convention will always target the primary key of the principal entity. Per fare riferimento a una chiave alternativa, è necessario eseguire una configurazione aggiuntiva usando l'API Fluent.To target an alternate key, additional configuration must be performed using the Fluent API.

Relazioni completamente definiteFully Defined Relationships

Il modello più comune per le relazioni prevede che le proprietà di navigazione siano definite in entrambe le estremità della relazione e in una proprietà di chiave esterna definita nella classe di entità dipendente.The most common pattern for relationships is to have navigation properties defined on both ends of the relationship and a foreign key property defined in the dependent entity class.

  • Se tra due tipi viene trovata una coppia di proprietà di navigazione, queste verranno configurate come proprietà di navigazione inversa della stessa relazione.If a pair of navigation properties is found between two types, then they will be configured as inverse navigation properties of the same relationship.

  • Se l'entità dipendente contiene una proprietà denominata <primary key property name>, <navigation property name><primary key property name>o <principal entity name><primary key property name>, sarà configurata come chiave esterna.If the dependent entity contains a property named <primary key property name>, <navigation property name><primary key property name>, or <principal entity name><primary key property name> then it will be configured as the foreign key.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

Avviso

Se sono presenti più proprietà di navigazione definite tra due tipi, ovvero più di una coppia distinta di spostamenti che puntano l'uno all'altro, non verrà creata alcuna relazione per convenzione e sarà necessario configurarle manualmente per identificare il modo in cui coppia proprietà di navigazione.If there are multiple navigation properties defined between two types (that is, more than one distinct pair of navigations that point to each other), then no relationships will be created by convention and you will need to manually configure them to identify how the navigation properties pair up.

Nessuna proprietà di chiave esternaNo Foreign Key Property

Sebbene sia consigliabile disporre di una proprietà di chiave esterna definita nella classe di entità dipendente, non è obbligatorio.While it is recommended to have a foreign key property defined in the dependent entity class, it is not required. Se non viene trovata alcuna proprietà di chiave esterna, verrà introdotta una proprietà di chiave esterna Shadow con il nome <navigation property name><principal key property name>. per ulteriori informazioni, vedere proprietà shadow .If no foreign key property is found, a shadow foreign key property will be introduced with the name <navigation property name><principal key property name> (see Shadow Properties for more information).

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

Proprietà di navigazione singolaSingle Navigation Property

Per includere una relazione definita per convenzione, è sufficiente includere una sola proprietà di navigazione (nessuna navigazione inversa e nessuna proprietà di chiave esterna).Including just one navigation property (no inverse navigation, and no foreign key property) is enough to have a relationship defined by convention. È inoltre possibile disporre di una sola proprietà di navigazione e di una proprietà di chiave esterna.You can also have a single navigation property and a foreign key property.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}

Eliminazione a catenaCascade Delete

Per convenzione, CASCADE DELETE verrà impostato su Cascade per le relazioni obbligatorie e ClientSetNull per le relazioni facoltative.By convention, cascade delete will be set to Cascade for required relationships and ClientSetNull for optional relationships. Cascade significa che vengono eliminate anche le entità dipendenti.Cascade means dependent entities are also deleted. ClientSetNull indica che le entità dipendenti che non vengono caricate in memoria rimarranno invariate e dovranno essere eliminate manualmente o aggiornate in modo da puntare a un'entità principale valida.ClientSetNull means that dependent entities that are not loaded into memory will remain unchanged and must be manually deleted, or updated to point to a valid principal entity. Per le entità caricate in memoria, EF Core tenterà di impostare le proprietà di chiave esterna su null.For entities that are loaded into memory, EF Core will attempt to set the foreign key properties to null.

Per la differenza tra le relazioni obbligatorie e facoltative, vedere la sezione relazioni obbligatorie e facoltative .See the Required and Optional Relationships section for the difference between required and optional relationships.

Per ulteriori informazioni sui diversi comportamenti di eliminazione e sulle impostazioni predefinite utilizzate dalla convenzione, vedere CASCADE DELETE .See Cascade Delete for more details about the different delete behaviors and the defaults used by convention.

Annotazioni dei datiData Annotations

Sono disponibili due annotazioni di dati che possono essere utilizzate per configurare relazioni, [ForeignKey] e [InverseProperty].There are two data annotations that can be used to configure relationships, [ForeignKey] and [InverseProperty]. Sono disponibili nello spazio dei nomi System.ComponentModel.DataAnnotations.Schema.These are available in the System.ComponentModel.DataAnnotations.Schema namespace.

ForeignKey[ForeignKey]

È possibile utilizzare le annotazioni dei dati per configurare quale proprietà deve essere utilizzata come proprietà di chiave esterna per una determinata relazione.You can use the Data Annotations to configure which property should be used as the foreign key property for a given relationship. Questa operazione viene in genere eseguita quando la proprietà di chiave esterna non viene individuata per convenzione.This is typically done when the foreign key property is not discovered by convention.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace EFModeling.DataAnnotations.Relationships.ForeignKey
{
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }

    #region Entities
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogForeignKey { get; set; }

        [ForeignKey("BlogForeignKey")]
        public Blog Blog { get; set; }
    }
    #endregion
}

Suggerimento

L'annotazione [ForeignKey] può essere posizionata su una proprietà di navigazione nella relazione.The [ForeignKey] annotation can be placed on either navigation property in the relationship. Non è necessario che venga eseguita la proprietà di navigazione nella classe di entità dipendente.It does not need to go on the navigation property in the dependent entity class.

[InverseProperty][InverseProperty]

È possibile utilizzare le annotazioni dei dati per configurare la modalità di associazione delle proprietà di navigazione nelle entità dipendenti e entità.You can use the Data Annotations to configure how navigation properties on the dependent and principal entities pair up. Questa operazione viene in genere eseguita quando è presente più di una coppia di proprietà di navigazione tra due tipi di entità.This is typically done when there is more than one pair of navigation properties between two entity types.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace EFModeling.DataAnnotations.Relationships.InverseProperty
{
    class MyContext : DbContext
    {
        public DbSet<Post> Posts { get; set; }
        public DbSet<User> Users { get; set; }
    }

    #region Entities
    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int AuthorUserId { get; set; }
        public User Author { get; set; }

        public int ContributorUserId { get; set; }
        public User Contributor { get; set; }
    }

    public class User
    {
        public string UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [InverseProperty("Author")]
        public List<Post> AuthoredPosts { get; set; }

        [InverseProperty("Contributor")]
        public List<Post> ContributedToPosts { get; set; }
    }
    #endregion
}

API FluentFluent API

Per configurare una relazione nell'API Fluent, è necessario innanzitutto identificare le proprietà di navigazione che compongono la relazione.To configure a relationship in the Fluent API, you start by identifying the navigation properties that make up the relationship. HasOne o HasMany identifica la proprietà di navigazione nel tipo di entità in cui si sta iniziando la configurazione.HasOne or HasMany identifies the navigation property on the entity type you are beginning the configuration on. È quindi possibile concatenare una chiamata a WithOne o WithMany per identificare la navigazione inversa.You then chain a call to WithOne or WithMany to identify the inverse navigation. HasOne/WithOne vengono utilizzate per le proprietà di navigazione di riferimento e HasMany/WithMany vengono utilizzate per le proprietà di navigazione della raccolta.HasOne/WithOne are used for reference navigation properties and HasMany/WithMany are used for collection navigation properties.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.FluentAPI.Relationships.NoForeignKey
{
    #region Model
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog)
                .WithMany(b => b.Posts);
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public Blog Blog { get; set; }
    }
    #endregion
}

Proprietà di navigazione singolaSingle Navigation Property

Se è presente una sola proprietà di navigazione, sono presenti overload senza parametri di WithOne e WithMany.If you only have one navigation property then there are parameterless overloads of WithOne and WithMany. Ciò indica che è concettualmente presente un riferimento o una raccolta nell'altra entità finale della relazione, ma nella classe di entità non è inclusa alcuna proprietà di navigazione.This indicates that there is conceptually a reference or collection on the other end of the relationship, but there is no navigation property included in the entity class.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.FluentAPI.Relationships.OneNavigation
{
    #region Model
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Blog>()
                .HasMany(b => b.Posts)
                .WithOne();
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
    }
    #endregion
}

Chiave esternaForeign Key

È possibile utilizzare l'API Fluent per configurare quale proprietà deve essere utilizzata come proprietà di chiave esterna per una determinata relazione.You can use the Fluent API to configure which property should be used as the foreign key property for a given relationship.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.Configuring.DataAnnotations.Samples.Relationships.ForeignKey
{
    #region Model
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog)
                .WithMany(b => b.Posts)
                .HasForeignKey(p => p.BlogForeignKey);
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogForeignKey { get; set; }
        public Blog Blog { get; set; }
    }
    #endregion
}

Nel listato di codice seguente viene illustrato come configurare una chiave esterna composta.The following code listing shows how to configure a composite foreign key.

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;

namespace EFModeling.Configuring.DataAnnotations.Samples.Relationships.CompositeForeignKey
{
    #region Model
    class MyContext : DbContext
    {
        public DbSet<Car> Cars { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Car>()
                .HasKey(c => new { c.State, c.LicensePlate });

            modelBuilder.Entity<RecordOfSale>()
                .HasOne(s => s.Car)
                .WithMany(c => c.SaleHistory)
                .HasForeignKey(s => new { s.CarState, s.CarLicensePlate });
        }
    }

    public class Car
    {
        public string State { get; set; }
        public string LicensePlate { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }

        public List<RecordOfSale> SaleHistory { get; set; }
    }

    public class RecordOfSale
    {
        public int RecordOfSaleId { get; set; }
        public DateTime DateSold { get; set; }
        public decimal Price { get; set; }

        public string CarState { get; set; }
        public string CarLicensePlate { get; set; }
        public Car Car { get; set; }
    }
    #endregion
}

È possibile utilizzare l'overload di stringa di HasForeignKey(...) per configurare una proprietà shadow come chiave esterna. per ulteriori informazioni, vedere proprietà shadow .You can use the string overload of HasForeignKey(...) to configure a shadow property as a foreign key (see Shadow Properties for more information). È consigliabile aggiungere esplicitamente la proprietà shadow al modello prima di utilizzarla come chiave esterna, come illustrato di seguito.We recommend explicitly adding the shadow property to the model before using it as a foreign key (as shown below).

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Add the shadow property to the model
        modelBuilder.Entity<Post>()
            .Property<int>("BlogForeignKey");

        // Use the shadow property as a foreign key
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey("BlogForeignKey");
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

Senza proprietà di navigazioneWithout Navigation Property

Non è necessario necessariamente fornire una proprietà di navigazione.You don't necessarily need to provide a navigation property. È possibile specificare semplicemente una chiave esterna su un lato della relazione.You can simply provide a Foreign Key on one side of the relationship.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFModeling.FluentAPI.Relationships.NoNavigation
{
    #region Model
    class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Post>()
                .HasOne<Blog>()
                .WithMany()
                .HasForeignKey(p => p.BlogId);
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
    }
    #endregion
}

Chiave principalePrincipal Key

Se si desidera che la chiave esterna faccia riferimento a una proprietà diversa dalla chiave primaria, è possibile utilizzare l'API Fluent per configurare la proprietà chiave principale per la relazione.If you want the foreign key to reference a property other than the primary key, you can use the Fluent API to configure the principal key property for the relationship. La proprietà configurata come chiave principale verrà automaticamente impostata come chiave alternativa. per ulteriori informazioni, vedere chiavi alternative .The property that you configure as the principal key will automatically be setup as an alternate key (see Alternate Keys for more information).

class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RecordOfSale>()
            .HasOne(s => s.Car)
            .WithMany(c => c.SaleHistory)
            .HasForeignKey(s => s.CarLicensePlate)
            .HasPrincipalKey(c => c.LicensePlate);
    }
}

public class Car
{
    public int CarId { get; set; }
    public string LicensePlate { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }

    public List<RecordOfSale> SaleHistory { get; set; }
}

public class RecordOfSale
{
    public int RecordOfSaleId { get; set; }
    public DateTime DateSold { get; set; }
    public decimal Price { get; set; }

    public string CarLicensePlate { get; set; }
    public Car Car { get; set; }
}

Nel listato di codice seguente viene illustrato come configurare una chiave principale composta.The following code listing shows how to configure a composite principal key.

class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RecordOfSale>()
            .HasOne(s => s.Car)
            .WithMany(c => c.SaleHistory)
            .HasForeignKey(s => new { s.CarState, s.CarLicensePlate })
            .HasPrincipalKey(c => new { c.State, c.LicensePlate });
    }
}

public class Car
{
    public int CarId { get; set; }
    public string State { get; set; }
    public string LicensePlate { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }

    public List<RecordOfSale> SaleHistory { get; set; }
}

public class RecordOfSale
{
    public int RecordOfSaleId { get; set; }
    public DateTime DateSold { get; set; }
    public decimal Price { get; set; }

    public string CarState { get; set; }
    public string CarLicensePlate { get; set; }
    public Car Car { get; set; }
}

Avviso

L'ordine in cui si specificano le proprietà della chiave principale deve corrispondere all'ordine in cui sono specificate per la chiave esterna.The order in which you specify principal key properties must match the order in which they are specified for the foreign key.

Relazioni obbligatorie e facoltativeRequired and Optional Relationships

È possibile usare l'API Fluent per configurare se la relazione è obbligatoria o facoltativa.You can use the Fluent API to configure whether the relationship is required or optional. In definitiva, controlla se la proprietà della chiave esterna è obbligatoria o facoltativa.Ultimately this controls whether the foreign key property is required or optional. Questa operazione è particolarmente utile quando si usa una chiave esterna dello stato di ombreggiatura.This is most useful when you are using a shadow state foreign key. Se nella classe di entità è presente una proprietà di chiave esterna, la richiesta della relazione viene determinata a seconda che la proprietà della chiave esterna sia obbligatoria o facoltativa (per ulteriori informazioni, vedere proprietà obbligatorie e facoltative ).If you have a foreign key property in your entity class then the requiredness of the relationship is determined based on whether the foreign key property is required or optional (see Required and Optional properties for more information).

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .IsRequired();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

Eliminazione a catenaCascade Delete

È possibile usare l'API Fluent per configurare in modo esplicito il comportamento di eliminazione a catena per una determinata relazione.You can use the Fluent API to configure the cascade delete behavior for a given relationship explicitly.

Vedere CASCADE DELETE nella sezione Saving data per una descrizione dettagliata di ogni opzione.See Cascade Delete on the Saving Data section for a detailed discussion of each option.

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .OnDelete(DeleteBehavior.Cascade);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int? BlogId { get; set; }
    public Blog Blog { get; set; }
}

Altri modelli di relazioneOther Relationship Patterns

Uno-a-unoOne-to-one

Le relazioni uno-a-uno hanno una proprietà di navigazione di riferimento su entrambi i lati.One to one relationships have a reference navigation property on both sides. Seguono le stesse convenzioni delle relazioni uno-a-molti, ma un indice univoco viene introdotto sulla proprietà della chiave esterna per garantire che solo un dipendente sia correlato a ogni entità.They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public BlogImage BlogImage { get; set; }
}

public class BlogImage
{
    public int BlogImageId { get; set; }
    public byte[] Image { get; set; }
    public string Caption { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

Nota

EF sceglierà una delle entità come dipendente in base alla capacità di rilevare una proprietà di chiave esterna.EF will choose one of the entities to be the dependent based on its ability to detect a foreign key property. Se l'entità sbagliata viene scelta come dipendente, è possibile usare l'API Fluent per correggere questa operazione.If the wrong entity is chosen as the dependent, you can use the Fluent API to correct this.

Quando si configura la relazione con l'API Fluent, si usano i metodi HasOne e WithOne.When configuring the relationship with the Fluent API, you use the HasOne and WithOne methods.

Quando si configura la chiave esterna è necessario specificare il tipo di entità dipendente. si noti il parametro generico fornito a HasForeignKey nell'elenco seguente.When configuring the foreign key you need to specify the dependent entity type - notice the generic parameter provided to HasForeignKey in the listing below. In una relazione uno-a-molti è evidente che l'entità con l'esplorazione dei riferimenti è il dipendente e quello con la raccolta è l'entità.In a one-to-many relationship it is clear that the entity with the reference navigation is the dependent and the one with the collection is the principal. Ma non si tratta di una relazione uno-a-uno, di conseguenza è necessario definirla in modo esplicito.But this is not so in a one-to-one relationship - hence the need to explicitly define it.

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<BlogImage> BlogImages { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .HasOne(p => p.BlogImage)
            .WithOne(i => i.Blog)
            .HasForeignKey<BlogImage>(b => b.BlogForeignKey);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public BlogImage BlogImage { get; set; }
}

public class BlogImage
{
    public int BlogImageId { get; set; }
    public byte[] Image { get; set; }
    public string Caption { get; set; }

    public int BlogForeignKey { get; set; }
    public Blog Blog { get; set; }
}

Molti-a-moltiMany-to-many

Le relazioni many-to-many senza una classe di entità per rappresentare la tabella di join non sono ancora supportate.Many-to-many relationships without an entity class to represent the join table are not yet supported. È tuttavia possibile rappresentare una relazione molti-a-molti includendo una classe di entità per la tabella di join ed eseguendo il mapping di due relazioni uno-a-molti separate.However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.

class MyContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Post)
            .WithMany(p => p.PostTags)
            .HasForeignKey(pt => pt.PostId);

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Tag)
            .WithMany(t => t.PostTags)
            .HasForeignKey(pt => pt.TagId);
    }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public List<PostTag> PostTags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public List<PostTag> PostTags { get; set; }
}

public class PostTag
{
    public int PostId { get; set; }
    public Post Post { get; set; }

    public string TagId { get; set; }
    public Tag Tag { get; set; }
}