2016-05-03 7 views
1

Gibt es einen besseren Weg, um dieses Ziel zu erreichen, leicht abfragbare (und einbindbare) Querschnitte einer verwandten number-entity in derselben Tabelle zu haben?EF7 Implementieren TPH + M2M

Ich begann ohne TPH in der Join-Tabelle zu implementieren, aber das macht den einen oder anderen Typ in Abfragen mehr beteiligt, afaict.

// table Related: [Id] 
public class Related 
{ 
    public Guid Id { get; set; } 
    public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>(); 
    public List<RelatedOtherOne> RelatedOtherOnes { get; set; } = new List<RelatedOtherOne>(); 
    public List<RelatedOtherTwo> RelatedOtherTwos { get; set; } = new List<RelatedOtherTwo>(); 
} 

// table RelatedOther: [RelatedId, OtherId, Type] 
public abstract class RelatedOther 
{ 
    public Guid RelatedId { get; set; } 
    public Guid OtherId { get; set; } 

    public Related Related { get; set; } 
    public Other Other { get; set; } 
    public abstract RelatedOtherType Type { get; } 
} 
public class RelatedOtherOne : RelatedOther 
{ 
    public override RelatedOtherType Type => RelatedOtherType.One; 
    // should be unnecessary, 'Other' should be correct type 
    public OtherOne OtherOne { get; set; } 
} 
public class RelatedOtherTwo : RelatedOther 
{ 
    public override RelatedOtherType Type => RelatedOtherType.Two; 
    // should be unnecessary, 'Other' should be correct type 
    public OtherTwo OtherTwo { get; set; } 
} 
public enum RelatedOtherType : int 
{ 
    One = 1, 
    Two = 2 
} 

// table Other: [Id, OneProp, TwoProp] 
public abstract class Other 
{ 
    public Guid Id { get; set; } 
    public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>(); 
} 
public class OtherOne : Other 
{ 
    public string OneProp { get; set; } 
} 
public class OtherTwo : Other 
{ 
    public string TwoProp { get; set; } 
} 

TPH ist like this
abgebildet M2M like this + Diskriminator in Haskey abgebildet wird()

Dies wird sogar noch komplizierter (wenn nicht unmöglich?) Wenn das Unternehmen entwickelt sich zu einer TPH-Strategie 'Verwandte' wie das andere'.

Antwort

0

Ich habe keine einfache Lösung, aber als ich über das gleiche Problem gestolpert bin, dachte ich, ich werde teilen, was ich bisher habe.

Ich fand heraus, dass ich normalerweise alle oder viele Arten der Beziehungen zu den Klassen einer TPH-Struktur laden muss.

Also verwende ich die Basis viele-zu-viele Klasse, um die verwandten Objekte zu laden. So ist diese Klasse nicht abstrakt sein kann:

public class Event2Location 
{ 
    [Required] 
    public Event Event { get; set; } 
    public int EventId { get; set; } 

    [Required] 
    public Location Location { get; set; } 
    public int LocationId { get; set; } 

    public byte EntityType { get; set; } 
} 

Die abgeleitete Klasse fügt nur einige Eigenschaften für einen leichteren Zugang:

public class Event2Country : Event2Location 
{ 
    [NotMapped] 
    public Country Country 
    { 
     get { return base.Location as Country; } 
     set { base.Location = value; } 
    } 

    [NotMapped] 
    public int CountryId 
    { 
     get { return base.LocationId; } 
     set { base.LocationId = value; } 
    } 
} 

In der Event Klasse I haben:

public virtual ICollection<Event2Location> Event2Locations { get; set; } 

[NotMapped] 
public virtual ICollection<Event2Country> Event2Countries => Event2Locations?.OfType<Event2Country>().ToList(); 
// I should probably add some caching here if accessed more often 

[NotMapped] 
public virtual ICollection<Event2City> Event2Cities => Event2Locations?.OfType<Event2City>().ToList(); 

Also, wenn ich laden die verbundenen Tabellen, die ich verwenden kann

.Include(e => e.Event2Locations).ThenInclude(j => j.Location) 

Und ich kann die Beziehungen eines bestimmten Typs bei Bedarf mit den NotMapped Collections zugreifen.

Ich benutze immer noch die abgeleiteten Event2 ... Klassen, um eine neue Beziehung hinzuzufügen.

Wie Sie sehen, habe ich eine Spalte EntityType zu der Viele-zu-Viele-Klasse hinzugefügt, die ich als TPH-Diskriminator verwende. Mit dieser Spalte kann ich auch angeben, welche Relationstypen ich laden möchte, wenn ich nicht alle laden möchte.

Das ist sicherlich nicht perfekt, aber ich habe es schließlich aufgegeben, das zu optimieren. First EFCore muss reifer werden. Zweitens möchte ich sehen, wie ich diese Strukturen tatsächlich nutze.

PS: Tatsächlich hat meine Standort-TPH-Struktur Eltern-Kind-Beziehungen in sich. Hier habe ich keine TPH-Struktur für die Beziehungsklasse angelegt (wie du gesagt hast - nicht möglich oder zumindest nicht sinnvoll). Ich habe ParentType und ChildType hinzugefügt. So kann ich bestimmen, welche Relationen ich tatsächlich laden möchte. Dann lade ich die zugehörigen Positionen der Typen, die ich manuell brauche, auf der Client-Seite aus dem Ergebnis.

+0

Ich entschied, dass, wenn die Enden der m2m-Assoziation TPH sind, die Join-Tabelle selbst diesen TPH widerspiegeln sollte, unabhängig davon, ob ich die Navigationseigenschaften für alle diese TPH-Typen auf beiden Seiten offenlege oder nicht. Es ist nur ein sauberes Design imo. Dies ermöglicht dann dem Framework, die Übersetzung der Navigationseigenschaft, die Sie als '[NotMapped]' verwenden, mit dem Cast zu behandeln. – JoeBrockhaus

+0

Und wenn ich 'abstract' auf meiner Basisklasse verwende, hindert es mich nicht daran, nur diesen Basistyp abzufragen - wenn die Abfrage mehr als einen abgeleiteten Typ als Ergebnismenge ergibt, instanziiert EF den korrekten abgeleiteten Typ und die Instanz in dem entsprechenden abgeleiteten Typ wird automatisch mit dieser Instanz gefüllt. Wenn Ihr Modell 2 (und nur 2) abgeleitete Typen hat, sollte die Basisklasse abstrakt sein. Eine Instanz von Event2Location ohne einen EventType-Diskriminatorwert macht keinen Sinn, richtig? Sie sollten daher niemals eine Vanilla-Instanz von Event2Location erstellen können. – JoeBrockhaus