Entity Framework
Zuordnungsbeziehung mit Entity Framework Code First: One-to-Many und Many-to-Many
Suche…
Einführung
In diesem Thema wird erläutert, wie Sie mithilfe von Entity Framework Code First Eins-zu-Viele- und Viele-zu-Viele-Beziehungen zuordnen können.
Eins-zu-Viele-Mapping
Nehmen wir also an, Sie haben zwei verschiedene Entitäten:
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; }
}
Und Sie möchten eine Eins-zu-Viele-Beziehung zwischen ihnen aufbauen, dh eine Person kann null, ein oder mehrere Autos haben und ein Auto gehört genau zu einer Person. Jede Beziehung ist bidirektional. Wenn eine Person ein Auto hat, gehört das Auto zu dieser Person.
Dazu modifizieren Sie einfach Ihre Modellklassen:
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; }
}
Und das ist es :) Du hast bereits eine Beziehung aufgebaut. In der Datenbank wird dies natürlich mit Fremdschlüsseln dargestellt.
Eins-zu-Viele-Karten: gegen die Konvention
Im letzten Beispiel können Sie sehen, dass EF herausfindet, in welcher Spalte sich der Fremdschlüssel befindet und wohin er verweisen soll. Wie? Mit Konventionen. Wenn Sie eine Eigenschaft des Typs Person
dem Namen Person
mit einer PersonId
Eigenschaft haben, kommt EF zu dem Schluss, dass PersonId
ein Fremdschlüssel ist, und verweist auf den Primärschlüssel der durch den Typ Person
dargestellten Tabelle.
Aber was , wenn Sie waren PersonId zu ownerID und Person an Vermieter im Autotyp ändern?
public class Car { public int CarId { get; set; } public string LicensePlate { get; set; } public int OwnerId { get; set; } public virtual Person Owner { get; set; } }
Nun, leider reichen die Konventionen in diesem Fall nicht aus, um das richtige DB-Schema zu erzeugen:
Keine Bange; Sie können EF mit einigen Hinweisen zu Ihren Beziehungen und Schlüsseln im Modell helfen. Einfach Ihren konfigurieren Car
- Typ die verwenden OwnerId
Eigenschaft als FK. Erstellen Sie eine Entitätstypkonfiguration und wenden Sie sie in OnModelCreating()
:
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasRequired(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
}
}
Dies besagt im Wesentlichen, dass Car
eine erforderliche Eigenschaft besitzt, Owner
( HasRequired () ), und in der Art des Owner
wird die Cars
Eigenschaft verwendet, um auf die Autoentitäten (WithMany () ) zurückzugreifen. Schließlich wird die Eigenschaft angegeben, die den Fremdschlüssel darstellt ( HasForeignKey () ). Dies gibt uns das Schema, das wir wollen:
Sie können die Beziehung auch von der Person
aus konfigurieren:
public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
public PersonEntityTypeConfiguration()
{
this.HasMany(p => p.Cars).WithRequired(c => c.Owner).HasForeignKey(c => c.OwnerId);
}
}
Die Idee ist die gleiche, nur die Seiten sind unterschiedlich (beachten Sie, wie Sie das Ganze lesen können: "Diese Person hat viele Autos, jedes Auto mit einem erforderlichen Besitzer"). Es spielt keine Rolle, ob Sie die Beziehung von der Person
oder der Car
aus konfigurieren. Sie können sogar beide einschließen, aber in diesem Fall müssen Sie auf beiden Seiten dieselbe Beziehung angeben!
Zuordnung von Null oder Eins-zu-Viele
In den vorherigen Beispielen kann ein Auto ohne eine Person nicht existieren. Was wäre, wenn Sie wollten, dass die Person von der Autoseite optional ist? Nun, es ist ziemlich einfach zu wissen, wie man eins zu viele macht. Ändern PersonId
einfach die PersonId
in Car
, dass sie nullwertfähig ist:
public class Car
{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public int? PersonId { get; set; }
public virtual Person Person { get; set; }
}
Und dann verwenden Sie HasOptional () (oder WithOptional () , abhängig von welcher Seite Sie die Konfiguration durchführen):
public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
public CarEntityTypeConfiguration()
{
this.HasOptional(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
}
}
Viel zu viel
Lassen Sie uns zu dem anderen Szenario übergehen, in dem jede Person mehrere Autos haben kann und jedes Auto mehrere Besitzer haben kann. Dies ist eine viele-zu-viele-Beziehung. Der einfachste Weg ist, EF seine Zauberei mit Konventionen zu überlassen.
Ändern Sie einfach das Modell wie folgt:
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; }
}
Und das Schema:
Fast perfekt. Wie Sie sehen, hat EF die Notwendigkeit eines Join-Tisches erkannt, an dem Sie die Personen-Auto-Paarungen verfolgen können.
Many-to-many: Anpassen der Join-Tabelle
Möglicherweise möchten Sie die Felder in der Join-Tabelle umbenennen, um ein wenig freundlicher zu sein. Sie können dies tun, indem Sie die üblichen Konfigurationsmethoden verwenden (wiederum spielt es keine Rolle, von welcher Seite Sie die Konfiguration vornehmen):
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");
}
);
}
}
Ganz einfach sogar zu lesen: Dieses Auto hat viele Besitzer ( HasMany () ), wobei jeder Besitzer viele Autos hat ( WithMany () ). Ordnen Sie dies so zu, dass Sie den linken Schlüssel OwnerId ( MapLeftKey () ), den rechten Schlüssel CarId ( MapRightKey () ) und das Ganze der Tabelle PersonCars ( ToTable () ) zuordnen . Und das gibt Ihnen genau dieses Schema:
Many-to-many: Benutzerdefinierte Join-Entität
Ich muss zugeben, ich bin nicht wirklich ein Fan davon, EF die Join-Tabelle ohne eine Join-Entität entnehmen zu lassen. Sie können keine zusätzlichen Informationen zu einer Personen-Auto-Vereinigung verfolgen (beispielsweise das Datum, ab dem sie gültig sind), da Sie die Tabelle nicht ändern können.
Außerdem ist die CarId
in der Join-Tabelle Teil des Primärschlüssels. Wenn die Familie ein neues Auto kauft, müssen Sie zuerst die alten Zuordnungen löschen und neue hinzufügen. EF verbirgt dies vor Ihnen, aber dies bedeutet, dass Sie diese beiden Vorgänge anstelle eines einfachen Updates ausführen müssen (ganz zu schweigen davon, dass häufiges Einfügen / Löschen zu Indexfragmentierung führen kann - gut, es gibt eine einfache Lösung ).
In diesem Fall können Sie eine Join-Entität erstellen, die sowohl auf ein bestimmtes Fahrzeug als auch auf eine bestimmte Person verweist. Grundsätzlich betrachten Sie Ihre Many-to-Many-Assoziation als Kombination zweier One-to-Many-Assoziationen:
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; }
}
Dies gibt mir viel mehr Kontrolle und ist viel flexibler. Ich kann jetzt benutzerdefinierte Daten zur Assoziation hinzufügen, und jede Assoziation verfügt über einen eigenen Primärschlüssel, sodass ich das Auto oder die Eigentümerreferenz darin aktualisieren kann.
Beachten Sie, dass dies wirklich nur eine Kombination aus zwei Eins-zu-Viele-Beziehungen ist. Sie können also alle Konfigurationsoptionen verwenden, die in den vorherigen Beispielen beschrieben wurden.