Code First: Unabhängige Assoziationen vs. Fremdschlüsselassoziationen?

102

Jedes Mal, wenn ich an einem neuen Projekt arbeite und meine POCOs entwerfe, habe ich eine mentale Debatte mit mir. Ich habe viele Tutorials / Codebeispiele gesehen, die Fremdschlüsselassoziationen zu bevorzugen scheinen :

Fremdschlüsselzuordnung

public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; } // <-- Customer ID
    ...
}

Im Gegensatz zu unabhängigen Vereinen :

Unabhängiger Verein

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Ich habe in der Vergangenheit mit NHibernate gearbeitet und unabhängige Assoziationen verwendet, die sich nicht nur mehr OO anfühlen, sondern auch (bei verzögertem Laden) den Vorteil haben, dass ich auf das gesamte Kundenobjekt anstatt nur auf dessen ID zugreifen kann. Auf diese Weise kann ich beispielsweise eine Order-Instanz abrufen und dann Order.Customer.FirstNameohne expliziten Join auskommen, was äußerst praktisch ist.

Um es noch einmal zusammenzufassen, meine Fragen sind:

  1. Gibt es signifikante Nachteile bei der Verwendung unabhängiger Verbände? und...
  2. Wenn es keine gibt, was wäre der Grund für die Verwendung von Fremdschlüsselzuordnungen?
Daniel Liuzzi
quelle

Antworten:

106

Wenn Sie ORM voll ausnutzen möchten, verwenden Sie auf jeden Fall die Entitätsreferenz:

public class Order
{
    public int ID { get; set; }
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Sobald Sie ein Entitätsmodell aus einer Datenbank mit FKs generieren, werden immer Entitätsreferenzen generiert. Wenn Sie sie nicht verwenden möchten, müssen Sie die EDMX-Datei manuell ändern und Eigenschaften hinzufügen, die FKs darstellen. Zumindest war dies in Entity Framework v1 der Fall, wo nur unabhängige Zuordnungen zulässig waren.

Entity Framework v4 bietet eine neue Art der Zuordnung, die als Fremdschlüsselzuordnung bezeichnet wird. Der offensichtlichste Unterschied zwischen der unabhängigen und der Fremdschlüsselzuordnung besteht in der Ordnungsklasse:

public class Order
{
    public int ID { get; set; }
    public int CustomerId { get; set; }  // <-- Customer ID
    public Customer Customer { get; set; } // <-- Customer object
    ...
}

Wie Sie sehen können, haben Sie sowohl eine FK-Eigenschafts- als auch eine Entitätsreferenz. Es gibt weitere Unterschiede zwischen zwei Arten von Assoziationen:

Unabhängiger Verein

  • Es wird als separates Objekt in dargestellt ObjectStateManager. Es hat seine eigenen EntityState!
  • Beim Aufbau einer Assoziation benötigen Sie immer Berechtigungen von beiden Enden der Assoziation
  • Diese Zuordnung wird auf dieselbe Weise wie eine Entität zugeordnet.

Fremdschlüsselzuordnung

  • Es wird in nicht als separates Objekt dargestellt ObjectStateManager. Aus diesem Grund müssen Sie einige spezielle Regeln befolgen.
  • Beim Aufbau einer Assoziation benötigen Sie nicht beide Enden der Assoziation. Es reicht aus, eine untergeordnete Entität und eine PK der übergeordneten Entität zu haben, aber der PK-Wert muss eindeutig sein. Wenn Sie die Fremdschlüsselzuordnung verwenden, müssen Sie auch neu generierten Entitäten, die in Beziehungen verwendet werden, temporäre eindeutige IDs zuweisen.
  • Diese Zuordnung wird nicht zugeordnet, sondern definiert referenzielle Einschränkungen.

Wenn Sie die Fremdschlüsselzuordnung verwenden möchten, müssen Sie im Assistenten für Entitätsdatenmodelle das Kontrollkästchen Fremdschlüsselspalten in das Modell einbeziehen aktivieren.

Bearbeiten:

Ich fand heraus, dass der Unterschied zwischen diesen beiden Arten von Assoziationen nicht sehr bekannt ist, deshalb schrieb ich einen kurzen Artikel , der dies mit mehr Details und meiner eigenen Meinung dazu behandelt.

Ladislav Mrnka
quelle
Vielen Dank für Ihre sehr aufschlussreiche Antwort und auch für die Hinweise zur richtigen Terminologie, die mir geholfen haben, zahlreiche Ressourcen zu diesem Thema und den Vor- und Nachteilen beider Techniken zu finden.
Daniel Liuzzi
1
Ich bin gerade auf Ihren Artikel gestoßen, Ladislav. Sehr interessante Lektüre und eine großartige Ressource, um den Unterschied zwischen diesen beiden Ansätzen besser zu verstehen. Prost.
Daniel Liuzzi
1
@GaussZ: Wie ich weiß, hat sich seit EF4 (wo FK-Assoziationen eingeführt wurden) nichts an der Art und Weise geändert, wie Assoziationen behandelt werden.
Ladislav Mrnka
1
Diese und die anderen Antworten scheinen die Leistungsbedenken nicht zu berühren. Gemäß Abschnitt 2.2 Faktoren, die die Leistung der Ansichtsgenerierung aus einem MSDN-Artikel beeinflussen, scheint die Verwendung der unabhängigen Zuordnung die Kosten für die Ansichtsgenerierung gegenüber Fremdschlüsselzuordnungen zu erhöhen.
Veverke
1
@LadislavMrnka: Kannst du überprüfen, ob der Link zu dem oben erwähnten Artikel funktioniert? Ich kann nicht darauf zugreifen.
Veverke
34

Verwende beide. Machen Sie Ihre Entitätsreferenzen virtuell, um ein verzögertes Laden zu ermöglichen. So was:

public class Order
{
  public int ID { get; set; }
  public int CustomerID { get; set; }
  public virtual Customer Customer { get; set; } // <-- Customer object
  ...
}

Dies erspart unnötige DB-Suchvorgänge, ermöglicht ein verzögertes Laden und ermöglicht das einfache Anzeigen / Festlegen der ID, wenn Sie wissen, wie sie aussehen soll. Beachten Sie, dass beides Ihre Tabellenstruktur in keiner Weise ändert.

Seth Paulson
quelle
5
Einverstanden. Das habe ich letztendlich getan, wie Ladislav vorgeschlagen hat. Es gibt Ihnen wirklich das Beste aus beiden Welten; das gesamte Objekt, wenn Sie alle seine Eigenschaften benötigen, und seine ID, wenn Sie nur die PK benötigen und sich nicht um den Rest kümmern.
Daniel Liuzzi
9

Unabhängige Assoziationen funktionieren nicht gut damit AddOrUpdate, was normalerweise in SeedMethoden verwendet wird. Wenn die Referenz ein vorhandenes Element ist, wird sie erneut eingefügt.

// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);

Das Ergebnis ist, dass der bestehende Kunde erneut eingefügt wird und der neue (neu eingefügte) Kunde einer neuen Bestellung zugeordnet wird.


Es sei denn, wir verwenden die Fremdschlüsselzuordnung und weisen die ID zu.

 // Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);

// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);

Wir haben das erwartete Verhalten, bestehende Kunden werden mit neuen Bestellungen verbunden.

Yuliam Chandra
quelle
2
Dies ist eine gute Erkenntnis. Die Problemumgehung (und ich denke der richtige Weg), einen Kunden an eine Bestellung anzuhängen, besteht jedoch darin, ihn aus dem Datenbankkontext wie folgt zu laden: var order = new Order { Id = 1, Customer = db.Customers.Find(1) }; Oder Sie können die Select-Methode verwenden, um den Kunden aus dem Datenbankkontext zu laden. Dies funktioniert mit unabhängiger Vereinigung.
Tala9999
4

Ich bevorzuge den Objektansatz, um unnötige Suchvorgänge zu vermeiden. Die Eigenschaftsobjekte können genauso einfach ausgefüllt werden, wenn Sie Ihre Factory-Methode aufrufen, um die gesamte Entität zu erstellen (unter Verwendung eines einfachen Rückrufcodes für verschachtelte Entitäten). Es gibt keine Nachteile, die ich sehen kann, außer der Speichernutzung (aber Sie würden Ihre Objekte zwischenspeichern, oder?). Alles, was Sie tun, ist, den Heap durch den Stack zu ersetzen und einen Leistungsgewinn zu erzielen, wenn Sie keine Suchvorgänge durchführen. Ich hoffe das macht Sinn.

CarneyCode
quelle