Sök…


Introduktion

Ämnet diskuterar hur du kan kartlägga en-till-många och många-till-många-relationer med Entity Framework Code först.

Kartlägga en-till-många

Så låt oss säga att du har två olika enheter, något liknande:

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

Och du vill skapa en en-till-många-relation mellan dem, det vill säga en person kan ha noll, en eller flera bilar, och en bil exakt tillhör en person. Varje förhållande är dubbelriktad, så om en person har en bil tillhör bilen personen.

För att göra detta ändrar du bara dina modellklasser:

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

Och det är det :) Du har redan skapat din relation. I databasen representeras detta naturligtvis med utländska nycklar.

Kartlägga en-till-många: mot konventet

I det sista exemplet kan du se att EF räknar ut vilken kolumn som är den utländska nyckeln och var ska den peka på. Hur? Genom att använda konventioner. Att ha en egenskap av typen Person som heter Person med en PersonId egenskap leder till att EF drar slutsatsen att PersonId är en utländsk nyckel, och den pekar på den primära nyckeln i tabellen som representeras av typen Person .

Men om du skulle ändra PersonId till OwnerId person till ägare i den typ 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; }
}

Nåväl, tyvärr i detta fall räcker inte konventionerna för att producera rätt DB-schema:

Inga problem; du kan hjälpa EF med några tips om dina relationer och nycklar i modellen. Helt enkelt konfigurera Car typ använda OwnerId egendom som FK. Skapa en enhetstypkonfiguration och tillämpa den i din OnModelCreating() :

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

Detta säger i princip att Car har en obligatorisk egendom, Owner ( HasRequired () ) och i typen av Owner används egenskapen Cars för att hänvisa tillbaka till bilenheterna (WithMany () ). Och slutligen anges egenskapen som representerar den utländska nyckeln ( HasForeignKey () ). Detta ger oss det schema vi vill ha:

Du kan också konfigurera förhållandet från Person :

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

Idén är densamma, bara sidorna är olika (notera hur du kan läsa hela saken: "den här personen har många bilar, varje bil med en nödvändig ägare"). Det spelar ingen roll om du konfigurerar förhållandet från Person eller Car . Du kan till och med inkludera båda, men i detta fall vara noga med att ange samma förhållande på båda sidor!

Kartlägga noll eller en-till-många

I de tidigare exemplen kan en bil inte existera utan en person. Tänk om du ville att personen ska vara valfri från bilsidan? Det är lite enkelt att veta hur man gör en-till-många. PersonId in Car att vara nullable:

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

Och använd sedan HasOptional () (eller WithOptional () , beroende på vilken sida du gör konfigurationen):

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

Många-till-många

Låt oss gå vidare till det andra scenariot, där varje person kan ha flera bilar och varje bil kan ha flera ägare (men återigen är förhållandet dubbelriktat). Detta är en förhållande mellan många och många. Det enklaste sättet är att låta EF göra det magi med konventioner.

Ändra bara modellen så här:

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

Och schemat:

Nästan perfekt. Som ni kan se, erkände EF behovet av ett sammanfogningstabell, där du kan hålla reda på kopplingar mellan personbilar.

Många till många: anpassa sammanfogningstabellen

Du kanske vill byta namn på fälten i sammanfogningstabellen för att bli lite mer vänlig. Du kan göra detta genom att använda de vanliga konfigurationsmetoderna (igen, det spelar ingen roll vilken sida du gör konfigurationen från):

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

Ganska lättläst även: denna bil har många ägare ( HasMany () ), där varje ägare har många bilar ( WithMany () ). Karta detta så att du kartlägger den vänstra nyckeln till ÄgareId ( MapLeftKey () ), den högra nyckeln till CarId ( MapRightKey () ) och hela saken till tabellen PersonCars ( ToTable () ). Och detta ger dig exakt det schemat:

Många-till-många: anpassad anslutande enhet

Jag måste erkänna att jag inte riktigt är en fan av att låta EF dra slutsatsen till kopplingstabellen utan en sammanslutningsenhet. Du kan inte spåra extra information till en person-bilförening (låt oss säga datumet från vilket det är giltigt), eftersom du inte kan ändra tabellen.

Dessutom är CarId i sammanfogningstabellen en del av den primära nyckeln, så om familjen köper en ny bil måste du först ta bort de gamla föreningarna och lägga till nya. EF döljer detta för dig, men det betyder att du måste göra dessa två åtgärder istället för en enkel uppdatering (för att inte tala om att ofta infogningar / borttagningar kan leda till indexfragmentering - bra att det finns en enkel lösning för det).

I det här fallet kan du skapa en sammanslutningsenhet som har en referens till både en specifik bil och en specifik person. I grund och botten ser du på din många-till-många förening som en kombination av två en-till-många föreningar:

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

Detta ger mig mycket mer kontroll och det är mycket mer flexibelt. Jag kan nu lägga till anpassade data till föreningen och varje förening har sin egen primära nyckel, så jag kan uppdatera bilen eller ägarreferensen i dem.

Observera att det här egentligen bara är en kombination av två förhållanden en till många, så att du kan använda alla konfigurationsalternativ som diskuterats i de tidigare exemplen.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow