2016-05-03 6 views
1

同じテーブルに格納されている関連する多対多エンティティの簡単なクエリー可能な(およびInclude-able)クロスセクションを持つこの最終目標を達成するためのより良い方法はありますか?EF7実装TPH + M2M

私は結合テーブルにTPHを実装せずに始めましたが、そのためにはクエリの中の1つのタイプまたは別のタイプが消費されてしまいます。

// 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は、M2Mは(HasKeyにlike this +弁別がマッピングされているlike this
マッピングされている)

これはさらに複雑になります(不可能ではない?) '関連' 実体のようなTPH戦略に進化「その他」

答えて

0

私は簡単な解決策はありませんが、私が同じ問題を遭遇したとき、私はこれまでのことを分かち合うと思っていました。

私は、通常、関係のすべてまたは多くをTPH構造のクラスにロードする必要があることを知りました。

したがって、多対多の基本クラスを使用して、関連するオブジェクトをロードします。したがって、このクラスは抽象的にすることはできません。

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; } 
} 

派生クラスだけ簡単にアクセスするためのいくつかのプロパティを追加します。私が持っているEventクラスで

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; } 
    } 
} 

を:

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(); 

だから私はロードするとき私が使用できる結合テーブル

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

そしてNotMappedコレクションでは、必要に応じて特定のタイプの関係にアクセスできます。

引き続き派生したEvent2 ...クラスを使用して新しい関係を追加します。

ご覧のとおり、EntityTypeをTPHディスクリミネータとして使用する多対多クラスに追加しました。この列では、すべてをロードしたくない場合に、ロードするリレーション/エンティティのタイプを宣言することもできます。

modelBuilder.Entity<Event2Location>() 
    .HasDiscriminator<byte>("EntityType") 
    .HasValue<Event2Location>(0) 
    .HasValue<Event2Country>(1) 

これは確かに完全ではありませんが、私はついにそれを最適化することを断念しました。最初のEFCoreはより成熟しなければなりません。次に、私が実際にこれらの構造をどのように使用しているかを見たいと思います。

PS:実際には、自分のロケーションのTPH構造に親子関係があります。ここでは、関係クラスのTPH構造を作成しませんでした(あなたが言ったように、不可能か、少なくとも合理的ではありません)。私はParentTypeとChildTypeを追加しました。したがって、私は実際にロードしたい関係を決定することができます。次に、結果からクライアント側で手動で必要とするタイプの関連Locationsを取得します。

+0

m2mアソシエーションの終わりがTPHの場合、それらのTPHタイプのいずれかの両端にナビゲーションプロパティを公開するかどうかにかかわらず、結合テーブル自体もそのTPHを反映する必要があります。それは単なるクリーンなデザインです。これにより、フレームワークはキャストを使って '[NotMapped]'として持っているNavigationプロパティの変換を処理できます。 – JoeBrockhaus

+0

そして、私の基底クラスで 'abstract'を使うと、その基底型だけを照会することができません。結果セットとして2つ以上の派生型が返された場合、EFは正しい派生型をインスタンス化し、対応する派生型のインスタンスに自動的にそのインスタンスが移入されます。モデルに2つの派生型がある場合、基本クラスは抽象クラスである必要があります。 EventType識別子の値を持たないEvent2Locationのインスタンスは実際には意味がありません。したがって、決してEvent2Locationのバニラインスタンスを作成することはできません。 – JoeBrockhaus

関連する問題