it-swarm.com.de

EF-Code zuerst: Soll ich die Navigationseigenschaften initialisieren?

Ich hatte einige Bücher gesehen (z. B. Programmier-Entity-Framework-Code zuerst Julia Lerman ), die ihre Domain-Klassen (POCO) ohne Initialisierung der Navigationseigenschaften definieren, wie z.

public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<Address> Address { get; set; }
    public virtual License License { get; set; }
}

einige andere Bücher oder Tools (z. B. Entity Framework Power Tools ) initialisieren beim Generieren von POCOs die Navigationseigenschaften der Klasse, z.

public class User
{
    public User()
    {
        this.Addresses = new IList<Address>();
        this.License = new License();
    }
    public int Id { get; set; }
    public string UserName { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
    public virtual License License { get; set; }
}

F1: Welches ist besser? Warum? Vor-und Nachteile?

Bearbeiten:

public class License
{
    public License()
    {
        this.User = new User();
    }
    public int Id { get; set; }
    public string Key { get; set; }
    public DateTime Expirtion { get; set; }

    public virtual User User { get; set; }
}

F2: Beim zweiten Ansatz würde ein Stapelüberlauf auftreten, wenn die Klasse `License` auch einen Verweis auf die Klasse` User` enthält. Dies bedeutet, dass wir eine Einwegreferenz haben sollten. (?) Wie sollen wir entscheiden, welche der Navigationseigenschaften entfernt werden sollen?

57

Sammlungen: Es spielt keine Rolle.

Es gibt einen deutlichen Unterschied zwischen Sammlungen und Referenzen als Navigationseigenschaften. Eine Referenz ist eine Entität. Eine Auflistung enthält Entitäten. Dies bedeutet, dass die Initialisierung einer Sammlung im Sinne der Geschäftslogik bedeutungslos ist: Sie definiert keine Zuordnung zwischen Entitäten. Eine Referenz setzen tut.

Es ist also nur eine Frage der Präferenz, ob oder wie Sie eingebettete Listen initialisieren.

Was das "Wie" betrifft, bevorzugen einige Leute eine verzögerte Initialisierung:

private ICollection<Address> _addresses;

public virtual ICollection<Address> Addresses
{ 
    get { return this._addresses ?? (this._addresses = new HashSet<Address>());
}

Es verhindert Nullreferenzausnahmen und erleichtert so das Testen und Manipulieren der Sammlung, verhindert aber auch unnötige Initialisierungen. Letzteres kann einen Unterschied machen, wenn eine Klasse relativ viele Sammlungen hat. Der Nachteil ist, dass es relativ viel Klempnerarbeit braucht, vor allem. im vergleich zu autoeigenschaften ohne initialisierung. Das Aufkommen des Null-Propagierungsoperators in C # hat es auch weniger dringend gemacht, Auflistungseigenschaften zu initialisieren.

... es sei denn, es wird explizit geladen

Das Einzige ist, dass beim Initialisieren von Sammlungen nur schwer zu überprüfen ist, ob eine Sammlung von Entity Framework geladen wurde. Wenn eine Sammlung initialisiert wird, wird eine Anweisung wie ...

var users = context.Users.ToList();

... erstellt User Objekte mit leeren, nicht-leeren Addresses Sammlungen (abgesehen vom verzögerten Laden). Um zu überprüfen, ob die Sammlung geladen ist, benötigen Sie Code wie ...

var user = users.First();
var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded;

Wenn die Auflistung nicht initialisiert ist, reicht eine einfache Prüfung von null aus. Wenn also das selektive explizite Laden ein wichtiger Teil Ihrer Codierungspraxis ist, d. H.

if (/*check collection isn't loaded*/)
    context.Entry(user).Collection(c => c.Addresses).Load();

... ist es möglicherweise bequemer, Auflistungseigenschaften nicht zu initialisieren.

Referenzeigenschaften: Nicht

Referenzeigenschaften sind Entitäten, daher ist es sinnvoll, ihnen ein leeres Objekt zuzuweisen.

Schlimmer noch, wenn Sie sie im Konstruktor initiieren, überschreibt EF sie nicht, wenn Sie Ihr Objekt materialisieren oder wenn Sie es verzögern. Sie haben immer ihre Anfangswerte, bis Sie sie aktiv ersetzen. Schlimmer noch, Sie könnten sogar leere Objekte in der Datenbank speichern!

Und es gibt noch einen weiteren Effekt: Beziehungskorrektur tritt nicht auf. Relationship Fixup ist der Prozess, mit dem EF alle Entitäten im Kontext über ihre Navigationseigenschaften verbindet. Wenn ein User und ein Licence separat geladen werden, bleibt User.License wird gefüllt und umgekehrt. Es sei denn natürlich, dass License im Konstruktor initialisiert wurde. Dies gilt auch für 1: n-Assoziationen. Wenn Address ein User in seinem Konstruktor initialisieren würde, User.Addresses wäre nicht besiedelt!

Entity Framework-Kern

Das Korrigieren von Beziehungen im Entity Framework-Kern (2.1 zum Zeitpunkt des Schreibens) wird durch initialisierte Referenznavigationseigenschaften in Konstruktoren nicht beeinflusst. Das heißt, wenn Benutzer und Adressen separat aus der Datenbank abgerufen werden, werden die Navigationseigenschaften aufgefüllt.
Beim verzögerten Laden werden jedoch die Eigenschaften der initialisierten Referenznavigation nicht überschrieben . Daher können auch beim Initialisieren von Referenznavigationseigenschaften in EF-Core-Konstruktoren Probleme auftreten. Tu es nicht. Es macht sowieso keinen Sinn,

70
Gert Arnold

In allen meinen Projekten folge ich der Regel - "Sammlungen sollten nicht null sein. Sie sind entweder leer oder haben Werte."

Das erste Beispiel ist möglich, wenn die Erstellung dieser Entitäten in der Verantwortung von Drittanbieter-Code (z. B. ORM) liegt und Sie an einem Kurzzeitprojekt arbeiten.

Zweites Beispiel ist besser, da

  • sie sind sicher, dass für die Entität alle Eigenschaften festgelegt wurden
  • du vermeidest dumme NullReferenceException
  • sie machen die Verbraucher Ihres Codes glücklicher

Personen, die domänengesteuertes Design praktizieren, machen Sammlungen als schreibgeschützt verfügbar und vermeiden es, Setter darauf zu setzen. (siehe Was ist die beste Vorgehensweise für schreibgeschützte Listen in NHibernate )

F1: Welches ist besser? Warum? Vor- und Nachteile?

Es ist besser, Nicht-Null-Spalten verfügbar zu machen, da Sie zusätzliche Überprüfungen in Ihrem Code vermeiden (z. B. Addresses). Es ist ein guter Vertrag, den Sie in Ihrer Codebasis haben. Für mich ist es jedoch in Ordnung, nullbare Verweise auf einzelne Entitäten verfügbar zu machen (z. B. License).

F2: Beim zweiten Ansatz würde ein Stapelüberlauf auftreten, wenn die Klasse License auch einen Verweis auf die Klasse User enthält. Dies bedeutet, dass wir eine Einwegreferenz haben sollten. (?) Wie sollen wir entscheiden, welche der Navigationseigenschaften entfernt werden sollen?

Als ich selbst Data Mapper Pattern entwickelte, versuchte ich, bidirektionale Referenzen zu vermeiden und hatte nur sehr selten eine Referenz von Kind zu Eltern.

Wenn ich ORMs verwende, ist es einfach, bidirektionale Referenzen zu haben.

Wenn es erforderlich ist, eine Testentität für meine Komponententests mit einem bidirektionalen Referenzsatz zu erstellen, befolge ich die folgenden Schritte:

  1. Ich baue parent entity mit Leerzeichen children collection.
  2. Dann füge ich evey child mit Bezug auf parent entity into children collection.

Wenn ich einen parameterlosen Konstruktor im Typ License hätte, würde ich die Eigenschaft user erforderlich machen.

public class License
{
    public License(User user)
    {
        this.User = user;
    }

    public int Id { get; set; }
    public string Key { get; set; }
    public DateTime Expirtion { get; set; }

    public virtual User User { get; set; }
}
4
Ilya Palkin

Es ist überflüssig für new die Liste, da Ihr POCO von Lazy Loading abhängt.

Beim verzögerten Laden wird eine Entität oder eine Sammlung von Entitäten automatisch aus der Datenbank geladen, wenn zum ersten Mal auf eine Eigenschaft zugegriffen wird, die sich auf die Entität/Entitäten bezieht. Bei Verwendung von POCO-Entitätstypen wird ein verzögertes Laden erreicht, indem Instanzen abgeleiteter Proxy-Typen erstellt und anschließend virtuelle Eigenschaften überschrieben werden, um den Lade-Hook hinzuzufügen.

Wenn Sie den virtuellen Modifikator entfernen würden, würden Sie das verzögerte Laden deaktivieren, und in diesem Fall würde Ihr Code nicht mehr funktionieren (da die Liste durch nichts initialisiert würde).

Beachten Sie, dass Lazy Loading eine Funktion ist, die vom Entity Framework unterstützt wird. Wenn Sie die Klasse außerhalb des Kontexts eines DbContext erstellen, leidet der abhängige Code offensichtlich unter einem NullReferenceException.

HTH

3
bas

F1: Welches ist besser? Warum? Vor-und Nachteile?

Die zweite Variante, wenn virtuelle Eigenschaften in einem Entitätskonstruktor festgelegt werden, weist ein bestimmtes Problem auf, das als " Aufruf eines virtuellen Mitglieds in einem Konstruktor " bezeichnet wird.

Wie bei der ersten Variante ohne Initialisierung der Navigationseigenschaften gibt es zwei Situationen, je nachdem, wer/was ein Objekt erstellt:

  1. Entity Framework erstellt ein Objekt
  2. Der Codekonsument erstellt ein Objekt

Die erste Variante ist vollkommen gültig, wenn Entity Framework ein Objekt erstellt, kann jedoch fehlschlagen, wenn ein Codekonsument ein Objekt erstellt.

Die Lösung, um sicherzustellen, dass ein Code-Consumer immer ein gültiges Objekt erstellt, ist die Verwendung einer statische Factory-Methode :

  1. Standardkonstruktor geschützt machen. Mit Entity Framework können Sie problemlos mit geschützten Konstruktoren arbeiten.

  2. Fügen Sie eine statische Factory-Methode hinzu, die ein leeres Objekt erstellt, z. Ein User -Objekt legt alle Eigenschaften fest, z. Addresses und License geben nach der Erstellung ein vollständig erstelltes User Objekt zurück

Auf diese Weise verwendet Entity Framework einen geschützten Standardkonstruktor, um ein gültiges Objekt aus Daten zu erstellen, die von einer Datenquelle stammen, und Code-Consumer verwendet eine statische Factory-Methode, um ein gültiges Objekt zu erstellen.

3
Lightman

Ich verwende die Antwort aus diesem Warum ist meine Proxy-Sammlung Entity Framework Code First null und warum kann ich sie nicht festlegen?

Hatte Probleme mit der Konstruktorinitialisierung. Ich mache das nur, um den Testcode zu vereinfachen. Wenn Sie sicherstellen, dass die Erfassung niemals null ist, ersparen Sie mir die ständige Initialisierung in Tests etc

2
GraemeMiller

Die anderen Antworten beantworten die Frage vollständig, aber ich möchte etwas hinzufügen, da diese Frage immer noch relevant ist und bei der Google-Suche vorkommt.

Wenn Sie den Assistenten "Code First Model From Database" in Visual Studio verwenden, werden alle Sammlungen folgendermaßen initialisiert:

public partial class SomeEntity
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public SomeEntity()
    {
        OtherEntities = new HashSet<OtherEntity>();
    }

    public int Id { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<OtherEntity> OtherEntities { get; set; }
}

Ich neige dazu, die Ausgabe von Assistenten als eine offizielle Empfehlung von Microsoft zu betrachten. Deshalb füge ich dieser fünf Jahre alten Frage hinzu. Daher Ich würde alle Sammlungen als HashSets initialisieren.

Und ich persönlich denke, es wäre ziemlich schwierig, die obigen Einstellungen zu optimieren, um die Vorteile der Autoeigenschaftsinitialisierer von C # 6.0 zu nutzen:

    public virtual ICollection<OtherEntity> OtherEntities { get; set; } = new HashSet<OtherEntity>();
1