Entity Framework
Kartlägga förhållandet med enhetsramkoden först: En-till-många och många-till-många
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.