Ricerca…


introduzione

L'argomento illustra come è possibile mappare le relazioni uno a molti e molti a molti utilizzando prima il codice di Entity Framework.

Mappatura uno-a-molti

Quindi diciamo che hai due entità diverse, qualcosa del genere:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
}

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

public class MyDemoContext : DbContext
{
   public DbSet<Person> People { get; set; }
   public DbSet<Car> Cars { get; set; }
}

E tu vuoi impostare una relazione uno-a-molti tra loro, cioè una persona può avere zero, una o più macchine e una macchina appartiene esattamente a una persona. Ogni relazione è bidirezionale, quindi se una persona ha una macchina, la macchina appartiene a quella persona.

Per fare ciò basta modificare le classi del tuo modello:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; } // don't forget to initialize (use HashSet)
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
}

E questo è tutto :) Hai già impostato il tuo rapporto. Nel database, questo è rappresentato con chiavi esterne, ovviamente.

Mappatura one-to-many: contro la convenzione

Nell'ultimo esempio, puoi vedere che EF identifica quale colonna è la chiave esterna e dove dovrebbe puntare. Come? Usando le convenzioni. Avere una proprietà di tipo Person che è denominata Person con una proprietà PersonId porta EF a concludere che PersonId è una chiave esterna e punta alla chiave primaria della tabella rappresentata dal tipo Person .

Ma cosa succede se dovessi cambiare PersonId in OwnerId e Person to Owner nel tipo Car ?

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int OwnerId { get; set; }
   public virtual Person Owner { get; set; }
}

Bene, purtroppo in questo caso, le convenzioni non sono sufficienti per produrre lo schema DB corretto:

Nessun problema; puoi aiutare EF con alcuni suggerimenti sulle tue relazioni e chiavi nel modello. Basta configurare il tipo di Car per utilizzare la proprietà OwnerId come FK. Crea una configurazione del tipo di entità e applicala nel tuo OnModelCreating() :

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasRequired(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Questo in pratica dice che l' Car ha una proprietà richiesta, Owner ( HasRequired () ) e nel tipo di Owner , la proprietà Cars viene utilizzata per fare riferimento alle entità auto ( WithMany () ). E infine viene specificata la proprietà che rappresenta la chiave esterna ( HasForeignKey () ). Questo ci dà lo schema che vogliamo:

È possibile configurare la relazione anche dal lato Person :

public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
  public PersonEntityTypeConfiguration()
  {
    this.HasMany(p => p.Cars).WithRequired(c => c.Owner).HasForeignKey(c => c.OwnerId);
  }
}

L'idea è la stessa, solo i lati sono diversi (si noti come si può leggere tutto: 'questa persona ha molte macchine, ogni macchina con un proprietario richiesto'). Non importa se si configura la relazione dal lato Person o dal lato Car . Puoi anche includerli entrambi, ma in questo caso fai attenzione a specificare la stessa relazione su entrambi i lati!

Mappatura zero o uno-a-molti

Negli esempi precedenti una macchina non può esistere senza una persona. Cosa succede se si desidera che la persona sia facoltativa dal lato auto? Bene, è abbastanza facile, sapendo come fare uno-a-molti. Basta cambiare il PersonId in Car per essere annullabile:

public class Car
{
    public int CarId { get; set; }
    public string LicensePlate { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }
}

Quindi utilizzare HasOptional () (o WithOptional () , a seconda del lato in cui si esegue la configurazione):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasOptional(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Molti-a-molti

Passiamo all'altro scenario, in cui ogni persona può avere più auto e ogni auto può avere più proprietari (ma, di nuovo, la relazione è bidirezionale). Questa è una relazione molti-a-molti. Il modo più semplice è lasciare che EF faccia la magia usando le convenzioni.

Basta cambiare il modello in questo modo:

 public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; }
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }        
   public virtual ICollection<Person> Owners { get; set; }
}

E lo schema:

Quasi perfetto. Come puoi vedere, EF ha riconosciuto la necessità di un tavolo join, in cui puoi tenere traccia degli abbinamenti auto-persona.

Molti-a-molti: personalizzazione della tabella di join

Potresti voler rinominare i campi nella tabella di join per essere un po 'più amichevoli. Puoi farlo usando i soliti metodi di configurazione (di nuovo, non importa da che parte si esegue la configurazione):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
   public CarEntityTypeConfiguration()
   {
      this.HasMany(c => c.Owners).WithMany(p => p.Cars)
          .Map(m =>
              {
                 m.MapLeftKey("OwnerId");
                 m.MapRightKey("CarId");
                 m.ToTable("PersonCars");
              }
        );
  }
}

Abbastanza facile da leggere anche: questa auto ha molti proprietari ( HasMany () ), con ogni proprietario che ha molte macchine ( WithMany () ). Mappalo per mappare il tasto sinistro su OwnerId ( MapLeftKey () ), il tasto giusto su CarId ( MapRightKey () ) e il tutto sulla tabella PersonCars (ToTable () ). E questo ti dà esattamente quello schema:

Molti-a-molti: entità di join personalizzata

Devo ammettere che non sono proprio un fan che permette a EF di dedurre la tabella di join senza un'entità join. Non puoi rintracciare informazioni extra su un'associazione persona-auto (diciamo la data da cui è valida), perché non puoi modificare la tabella.

Inoltre, il CarId nella tabella di join fa parte della chiave primaria, quindi se la famiglia acquista una nuova auto, devi prima eliminare le vecchie associazioni e aggiungerne di nuove. EF ti nasconde questo, ma questo significa che devi eseguire queste due operazioni invece di un semplice aggiornamento (per non parlare del fatto che inserimenti / eliminazioni frequenti potrebbero portare alla frammentazione dell'indice - buona cosa c'è una soluzione semplice per questo).

In questo caso, ciò che puoi fare è creare un'entità join che abbia un riferimento sia ad una specifica auto che a una persona specifica. Fondamentalmente si guarda all'associazione many-to-many come a una combinazione di due associazioni one-to-many:

public class PersonToCar
{
   public int PersonToCarId { get; set; }
   public int CarId { get; set; }
   public virtual Car Car { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
   public DateTime ValidFrom { get; set; }
}

public class Person
{
  public int PersonId { get; set; }
  public string Name { get; set; }
  public virtual ICollection<PersonToCar> CarOwnerShips { get; set; }
}

public class Car
{
  public int CarId { get; set; }
  public string LicensePlate { get; set; }        
  public virtual ICollection<PersonToCar> Ownerships { get; set; }
}

public class MyDemoContext : DbContext
{
  public DbSet<Person> People { get; set; }
  public DbSet<Car> Cars { get; set; }
  public DbSet<PersonToCar> PersonToCars { get; set; }
}

Questo mi dà molto più controllo ed è molto più flessibile. Ora posso aggiungere dati personalizzati all'associazione e ogni associazione ha la sua chiave primaria, quindi posso aggiornare l'auto o il riferimento del proprietario al loro interno.

Si noti che questa è solo una combinazione di due relazioni uno-a-molti, quindi è possibile utilizzare tutte le opzioni di configurazione discusse negli esempi precedenti.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow