Suche…


Einführung

In diesem Thema wird beschrieben, wie One-to-One-Beziehungen mithilfe von Entity Framework abgebildet werden.

Zuordnung von Eins zu Null oder Eins

Sagen wir noch einmal, dass Sie folgendes Modell haben:

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 jetzt möchten Sie es so einrichten, dass Sie die folgende Spezifikation ausdrücken können: Eine Person kann ein oder null Auto haben, und jedes Auto gehört genau zu einer Person (Beziehungen sind bidirektional, wenn CarA zu PersonA gehört, dann gehört PersonA dazu.) 'CarA).

Lassen Sie uns das Modell ein wenig ändern: Fügen Sie die Navigationseigenschaften und die Fremdschlüsseleigenschaften hinzu:

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

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 die Konfiguration:

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasRequired(c => c.Person).WithOptional(p => p.Car);                        
  }
}    

Zu diesem Zeitpunkt sollte dies selbsterklärend sein. Das Auto hat eine erforderliche Person ( HasRequired () ), wobei die Person ein optionales Auto hat ( WithOptional () ). Auch hier spielt es keine Rolle, von welcher Seite aus Sie diese Beziehung konfigurieren. Seien Sie vorsichtig, wenn Sie die richtige Kombination aus Has / With und Required / Optional verwenden. Auf der Person würde es so aussehen:

public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
  public PersonEntityTypeConfiguration()
  {
     this.HasOptional(p => p.Car).WithOptional(c => c.Person);                        
  }
}    

Jetzt schauen wir uns das Datenbankschema an:

Schauen Sie genau hin: Sie können sehen, dass es in People keinen FK gibt, der sich auf Car bezieht. Außerdem ist der FK in Car nicht die PersonId , sondern die CarId . Hier ist das eigentliche Skript für den FK:

ALTER TABLE [dbo].[Cars]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Cars_dbo.People_CarId] FOREIGN KEY([CarId])
REFERENCES [dbo].[People] ([PersonId])

Dies bedeutet, dass die Schlüsseleigenschaften CarId und PersonId foregn, die wir im Modell haben, grundsätzlich ignoriert werden. Sie befinden sich in der Datenbank, sind jedoch, wie erwartet, keine Fremdschlüssel. Das liegt daran, dass One-to-One-Mappings das Hinzufügen von FK in Ihrem EF-Modell nicht unterstützen. One-to-One-Mappings sind in einer relationalen Datenbank ziemlich problematisch.

Die Idee ist, dass jede Person genau ein Auto haben kann, und dass Auto nur dieser Person gehören kann. Oder es gibt Personendatensätze, denen keine Autos zugeordnet sind.

Wie konnte dies mit Fremdschlüsseln dargestellt werden? Offensichtlich könnte es eine PersonId in Car und eine CarId in People . Um zu erzwingen, dass jede Person nur ein Auto haben kann, muss PersonId in Car eindeutig sein. Wenn jedoch PersonId in People eindeutig ist, wie können Sie dann zwei oder mehr Datensätze hinzufügen, bei denen PersonId NULL (mehr als ein Auto ohne Besitzer)? Antwort: Das können Sie nicht (eigentlich können Sie in SQL Server 2008 und neuer einen gefilterten eindeutigen Index erstellen, aber vergessen Sie diese technischen Aspekte für einen Moment; ganz zu schweigen von anderen RDBMS). Ganz zu schweigen von dem Fall, in dem Sie beide Enden der Beziehung angeben ...

Die einzige Möglichkeit, diese Regel zu erzwingen, wenn die Tabellen People und Car denselben Primärschlüssel haben (gleiche Werte in den verbundenen Datensätzen). Und um dies zu erreichen, muss CarId in Car sowohl PK als auch FK für die PK of People sein. Und das macht das ganze Schema zu einem Chaos. Wenn ich dies verwende, PersonId ich den PK / FK lieber in Car PersonId und konfiguriere ihn entsprechend:

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

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

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasRequired(c => c.Person).WithOptional(p => p.Car);
     this.HasKey(c => c.PersonId);
  }
}

Nicht ideal, aber vielleicht ein bisschen besser. Bei der Verwendung dieser Lösung müssen Sie jedoch wachsam sein, da dies gegen die üblichen Namenskonventionen verstößt, die Sie in die Irre führen können. Hier ist das aus diesem Modell generierte Schema:

Daher wird diese Beziehung nicht vom Datenbankschema erzwungen, sondern von Entity Framework selbst. Deshalb müssen Sie sehr vorsichtig sein, wenn Sie dies verwenden, um niemanden direkt mit der Datenbank abkühlen zu lassen.

Eins-zu-eins-Mapping

Eins-zu-Eins-Mapping (wenn beide Seiten erforderlich sind) ist auch eine schwierige Sache.

Stellen wir uns vor, wie dies mit Fremdschlüsseln dargestellt werden kann. Wieder eine CarId in People , die sich auf CarId in Car bezieht, und eine PersonId in Car, die sich auf die PersonId in People bezieht.

Was passiert nun, wenn Sie einen Fahrzeugdatensatz einfügen möchten? Damit dies gelingt, muss in diesem Fahrzeugdatensatz eine PersonId angegeben sein, da dies erforderlich ist. Damit diese PersonId gültig ist, muss der entsprechende Datensatz in People vorhanden sein. OK, also lasst uns fortfahren und den Personendatensatz einfügen. Damit dies gelingt, muss eine gültige CarId im Personenrekord vorhanden sein - aber das Auto ist noch nicht eingefügt! Dies kann nicht der Fall sein, weil wir zuerst den Datensatz der betreffenden Person einfügen müssen. Wir können den referenzierten Personendatensatz jedoch nicht einfügen, da er auf den Fahrzeugdatensatz zurückgreift, so dass dieser zuerst eingefügt werden muss (Fremdschlüsselaufzeichnung :).

Dies kann auch nicht auf „logische“ Weise dargestellt werden. Wieder müssen Sie einen der Fremdschlüssel ablegen. Welches Sie fallen lassen, liegt bei Ihnen. Die mit einem Fremdschlüssel belegte Seite wird als "abhängig" bezeichnet, die Seite, die keinen Fremdschlüssel enthält, wird als "Principal" bezeichnet. Um die Eindeutigkeit der Abhängigen zu gewährleisten, muss die PK die FK sein. Das Hinzufügen einer FK-Spalte und das Importieren der Spalte in Ihr Modell wird nicht unterstützt.

Also hier ist die Konfiguration:

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
    this.HasRequired(c => c.Person).WithRequiredDependent(p => p.Car);
    this.HasKey(c => c.PersonId);
  }
}

Inzwischen sollten Sie wirklich die Logik dazu bekommen haben :) Denken Sie daran, dass Sie auch die andere Seite wählen können. Seien Sie nur vorsichtig, wenn Sie die abhängigen / Hauptversionen von WithRequired verwenden (und Sie müssen die PK in Car noch konfigurieren).

public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
  public PersonEntityTypeConfiguration()
  {
    this.HasRequired(p => p.Car).WithRequiredPrincipal(c => c.Person);
  }
}

Wenn Sie das DB-Schema überprüfen, werden Sie feststellen, dass es genau dasselbe ist wie bei der Eins-zu-Eins-Lösung oder der Nulllösung. Das liegt daran, dass dies wiederum nicht vom Schema, sondern von EF selbst erzwungen wird. Also nochmal, sei vorsichtig :)

Zuordnung von Eins oder Null-zu-Eins oder Null

Zum Abschluss betrachten wir kurz den Fall, wenn beide Seiten optional sind.

Inzwischen sollten Sie sich mit diesen Beispielen wirklich langweilig fühlen :), also gehe ich nicht ins Detail und spiele mit der Idee, zwei FKs und mögliche Probleme zu haben, und warne Sie vor den Gefahren, wenn Sie diese Regeln nicht durchsetzen Schema aber nur in EF selbst.

Hier ist die Konfiguration, die Sie anwenden müssen:

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
    this.HasOptional(c => c.Person).WithOptionalPrincipal(p => p.Car);
    this.HasKey(c => c.PersonId);
  }
}

Sie können auch von der anderen Seite aus konfigurieren, achten Sie nur auf die richtigen Methoden :)



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow