2017-08-30 1 views
2

Einfach, in C# EF6, wie ordnen Sie zwei Navigationseigenschaften derselben Tabelle zu, während ihre Ergebnissätze getrennt bleiben? Im einfachen Englisch habe ich eine Klasse, die ich zwei Sammlungen in einer anderen Klasse haben möchte. Mit anderen Worten, ich möchte zwei Sammlungen desselben Typs, aber mit verschiedenen Elementen. Unglücklicherweise scheint EF6 beide Sammlungen gleich zu behandeln und gibt ihnen beide die gleichen Elemente (jeden Datensatz in der Tabelle).C# EF6: Zwei Navigationseigenschaften des gleichen Typs

Das Beste, was ich von Dutzenden von StackOverflow Antworten gefunden habe, war dies, aber es hat das beschriebene Problem. In diesem Beispiel hat ein Vater viele Sons und viele Töchter, und sie haben jeweils derjenige Vater. Idealerweise können sowohl Söhne als auch Töchter in derselben Tabelle gespeichert werden Kind.

class Father 
{ 
    [Key] 
    public long Id { get; set; } 

    public virtual ICollection<Child> Sons { get; set; } 

    public virtual ICollection<Child> Daughters { get; set; } 
} 

class Child 
{ 
    [Key] 
    public long Id { get; set; } 

    public long FatherId_1 { get; set; } 

    public Father Father_1 { get; set; } 

    public long FatherId_2 { get; set; } // One for each collection??? 

    public Father Father_2 { get; set; } 
} 

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<Child>() 
      .HasRequired(e => e.Father_1) 
      .WithMany(e => e.Sons) 
      .HasForeignKey(e => e.FatherId_1); 
    modelBuilder.Entity<Child>() 
      .HasRequired(e => e.Father_2) 
      .WithMany(e => e.Daughters) 
      .HasForeignKey(e => e.FatherId_2); 
} 

Das Problem dabei ist, wenn die Daten wieder aus der Kindern Tabelle zu lesen, es unterscheidet nicht zwischen Sons und Töchtern. Das heißt, die Sons Sammlung wird nicht nur enthalten Sons sondern auch Töchter, und so wird die Töchter Sammlung. Ich hätte erwartet, dass EF6 versucht, eine Diskriminator-Spalte zu verwenden, aber das tut es nicht.

Frage: Wie ordnen Sie zwei Navigationseigenschaften derselben Tabelle zu und können die Datensätze weiterhin in die entsprechenden Navigationseigenschaften einlesen? Oder ist das Beispiel korrekt und mein Problem ist anderswo? Oder ist das nicht möglich, und sie müssen ihren eigenen Tabellen zugeordnet werden (mit identischen Schemas).

Antwort

1

Ich bin ein wenig verwirrt, mit Ihrer Erklärung. Aber ich fand einen Code für das, was ich verstehe:

für „Es ist ein Modell‚Child‘, die in zwei Sammlungen aufgeteilt werden muss“:

modelBuilder.Entity<Child>() 
.Map(m => 
    { 
    m.Properties(t => new { t.Id /*other props*/ }); 
    m.ToTable("Sons"); 
    }) 
.Map(m => 
    { 
    m.Properties(t => new { t.Id /*other props*/}); 
    m.ToTable("Daughters"); 
    }); 
+0

Es ist ein Modell "Child", das in zwei Sammlungen aufgeteilt werden muss, nicht zwei Modelle, die in einer Tabelle gespeichert werden müssen. Warum würden Sie zwei Klassen für denselben logischen Typ in Ihrem Modell erstellen? Können Sie zeigen, wie Sie das obige Problem im Code lösen würden? – drifter

+0

Wenn ich das richtig lese, ordnet es einige Eigenschaften einer Tabelle und andere Eigenschaften einer anderen Tabelle zu. Ist das richtig? Ich habe mein Problem im ersten Absatz geklärt. Grundsätzlich habe ich eine Klasse, die ich zwei Sammlungen in einer anderen Klasse haben möchte. Dies scheint ein allgemeines Muster zu sein. Ich bin verwirrt, warum es wenig Information darüber gibt. – drifter

0

ich einen Weg gefunden, um dieses Problem zu lösen, indem ein mit domain object backed by a state object. Im Grunde verwenden Sie ein Zustandsobjekt, um die Daten so zu speichern, wie es EF mag, und Ihr Domänenobjekt stellt diese Daten dem Rest Ihrer Anwendung zur Verfügung. Zum Beispiel:

public class Father 
{ 
    //--- Constructor --- 

    internal Father(FatherState state) 
    { 
     State = state; 
    } 

    //--- Properties --- 

    public long Id => State.Id; 

    public IList<Child> Sons => Children.Where(child => child.Type == ChildType.Son).ToList().AsReadOnly(); 

    public IList<Child> Daughters => Children.Where(child => child.Type == ChildType.Daughter).ToList().AsReadOnly(); 

    //--- Methods --- 

    public void AddChild(Child child) 
    { 
     State.Children.Add(child); 
    } 

    public void RemoveChild(Child child) 
    { 
     State.Children.Remove(child); 
    } 
} 

internal class FatherState 
{ 
    [Key] 
    public long Id { get; set; } 

    public virtual ICollection<Child> Children { get; set; } 
} 

public class Child 
{ 
    [Key] 
    public long Id { get; set; } 

    public long FatherId { get; set; } 

    public Father Father { get; set; } 

    public ChildType Type { get; set; } 
} 

public enum ChildType 
{ 
    Son, 
    Daughter 
} 

Natürlich kann dies nur mit dem Repository-Muster verwendet werden, da es das FatherState Objekt von EF in das Father Objekt durch die Anwendung verbraucht bereitgestellt übersetzen muss.

Ich denke, die wirkliche Lösung ist, auf eine fully featured ORM like NHibernate zu wechseln. EF ist viel zu unterentwickelt, und das wird mit .NET Core nur noch schlimmer. Ich sehe nicht einmal, wie es möglich ist, mit EF richtig DDD zu machen. Viele Entwickler müssen Kompromisse bei ihren Modellen eingehen. NHibernate hat viele Vorteile, und der einzige Nachteil, den ich gefunden habe, ist es bietet keine asynchronen Methoden, aber es gibt one oder two Argumente dagegen gemacht, und es can be done anyway. Es gibt auch forks von NHibernate, die sie als letztes Mittel zur Verfügung stellen.

0

Ich denke, dass Sie keine separaten Zuordnungen benötigen, um denselben Vater für Sohn und Tochter zu verzweigen. Die bessere Lösung ist, eine eindeutige Kennung in Child Table wie enum als vorherige Antwort schlägt vor, oder fügen Sie ein Gender Feld in Ihrem Child Modell und behalten Sie nur eine Zuordnung von Father Modell. (Ich kenne Ihre genauen Anforderungen nicht, aber Father, Child kann auch in einer Superklasse human oder person in strengen objektorientierten Begriffen verallgemeinert werden).

Aber da ich mir Ihrer Anforderungen nicht sicher bin, wenn Sie wirklich mit Ihrem Mapping fortfahren wollen, dann ist die Lösung, die ich vorschlage, die folgende.

class Father 
    { 
     [Key] 
     public long Id { get; set; } 

     public int SonId{get;set;} 
     public int DaughterId{get;set;} 


     [ForeignKey("SonId")] 
     public virtual Child Child_Son{get;set;} 

     [ForeignKey("DaughterId")] 
     public virtual Child Child_Son{get;set;} 
    } 

    class Child 
    { 
     [Key] 
     public long Id { get; set; } 

     public string Gender{get;set;} 
    } 

Erklärung:

Zwei Fremdschlüssel für jeweils Son und Daughter der gleichen Child Klasse in Father Klasse werden Sie leicht getrennte Sammlungen mit einfachen Linq oder jede SQL-Abfrage erreichen helfen. Darüber hinaus behält dies die Regel "Wenn Eltern existiert nur dann Kind existiert".

+0

Danke, ich werde das versuchen und melden. – drifter

+0

Ich hatte Probleme, es zu implementieren, aber ich kann es nicht bestätigen oder leugnen. Ich könnte es zur Arbeit gebracht haben, wenn ich länger versuchte. Ich bin knapp bei der Zeit und entschied mich für die Zuordnung aller Datensätze zu einer einzigen ICollection-Eigenschaft, dann verwende ich Accessor-Eigenschaften, um die Ergebnisse zu teilen und zu sortieren (ähnlich wie bei meiner Antwort). Es hält mein Datenbankdesign sauber und der Code ist einfach zu verstehen. – drifter

Verwandte Themen