2011-04-27 12 views
0

新しいEFコードファーストの使い方を理解しようとしていますが、Includeの機能を半汎用リポジトリクラスに適合させる方法を考えていません。 (私は、セミジェネリックと言うのは、クラスがジェネリックではなくメソッドだけなので、基本的には集合体をラップするリポジトリが1つだけでなく、その集合体の一部であるすべてのエンティティと対話できます。 )(半)汎用リポジトリに含まれています

私の状況は私が常にニーズがロードされることを一人の子供を持つエンティティを持っている、とだけ、必要に応じてロードされている他の子供たちは、私は私のリポジトリ法上の簡単なブールパラメータを含めます。これと同様に:私のベースのリポジトリである、と私は、派生クラスのものXyzIncludeメソッドをオーバーライドできるようにしたいと思い

public S GetByID<S>(int entityID, bool loadChildren = false) where S : class 
{ 
    DbSet<S> set = _context.Set<S>(); 

    if (loadChildren) 
    set = FullInclude<S>(set); 
    else 
    set = DefaultInclude<S>(set); 

    return set.Find(entityID); 
} 

protected virtual DbSet<S> DefaultInclude<S>(DbSet<S> set) where S : class 
{ 
    // base implementation just returns the set 
    // derived versions would attach Includes to the set before returning it 
    return set; 
} 

protected virtual DbSet<S> FullInclude<S>(DbSet<S> set) where S : class 
{ 
    // base implementation just returns the set 
    // derived versions would attach Includes to the set before returning it 
    return set; 
} 

。しかし、それらを非ジェネリックな実装でオーバーライドする必要があります。これは明らかに言語レベルでは機能しません。

これはLinq2Sqlでは、DataLoadOptionsオブジェクトを設定してコンテキストにアタッチするだけで簡単でした。 Include APIがDbSetになっていないので、これを行うにはどのように最善を尽くすのか困っています。戦略パターンなどの簡単な実装に関する推奨事項を探しています。

編集:私の質問の本質は、実際に非ジェネリックな派生バージョンでジェネリックメソッドをオーバーライドすることだと思います。私はそれを可能にするパターンまたは技術を探しています。拡張メソッドは私が見ている一つのことですが、誰かがあればもっと純粋なソリューションを好むでしょう。

答えて

0

はちょうど私が私がなってしまった溶液を用いてこれを再検討しようと思いました。私のリポジトリは完全に汎用的ではありませんが、すべてのメソッドがあるので、どのエンティティタイプのインクルードも格納し、そのメソッドが呼び出されたときにインクルードできる方法が必要でした。

私はLadislavの回答hereから借用しましたが、派生リポジトリで定義された個々のメソッドをそれぞれ独自のインクルードを定義するように再設計することがあります。いくつかの場所で同じインクルードを定義するコードのほんの一部はおそらくそれに値するでしょう。しかし、いずれにせよ、これは現在の設計であり、それは動作します...

ベースのリポジトリ:

public abstract class Repository : IQueryableRepository, IWritableRepository 
{ 
    private readonly DbContext _context; 
    private readonly Dictionary<Type, LambdaExpression[]> _includes = new Dictionary<Type, LambdaExpression[]>(); 

    protected Repository(DbContextBase context) 
    { 
    _context = context; 
    RegisterIncludes(_includes); 
    } 

    protected abstract void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes); 

    protected S GetSingle<S>(Expression<Func<S, bool>> query, bool getChildren = false) where S : class 
    { 
    IQueryable<S> entities = _context.Set<S>().AsNoTracking(); 

    if (query != null) 
     entities = entities.Where(query); 

    entities = ApplyIncludesToQuery<S>(entities, getChildren); 

    return entities.FirstOrDefault(); 
    } 

    private IQueryable<S> ApplyIncludesToQuery<S>(IQueryable<S> entities, bool getChildren) where S : class 
    { 
    Expression<Func<S, object>>[] includes = null; 

    if (getChildren && _includes.ContainsKey(typeof(S))) 
     includes = (Expression<Func<S, object>>[])_includes[typeof(S)]; 

    if (includes != null) 
     entities = includes.Aggregate(entities, (current, include) => current.Include(include)); 

    return entities; 
    } 
} 

派生リポジトリが唯一の彼らが一つの場所に含まれて定義する必要があり、その後、クエリメソッドあなたを呼び出すとき子どもが含まれるかどうかを指定してください(上記のgetChildrenを参照)。

public class DerivedRepository : Repository 
{ 
    public DerivedRepository(DbContext context) 
    : base(context) { } 

    protected override void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes) 
    { 
    includes.Add(typeof(ParentType), new Expression<Func<ParentType, object>>[] { 
     p => p.SomeChildReference.SomeGrandchild, 
     p => p.SomeOtherChildReference 
    }); 
    } 
} 
+0

これは興味深いアプローチですが、私があまり気にしていないことは、インクルードが1か所にあることです。私がそれを持っている問題は、あなたが状況に応じて異なることを望むかもしれないということです。ポケットベルの顧客のリストを取得している間は、プロフィール、投稿、請求書の情報は必要ありませんが、ユーザーの詳細ページではすべての情報が必要です –

+0

それは私の頭の中でちょっと議論したジレンマですこれを行う最善の方法を理解しようとしています。私は今のところ最も単純なオプションを使っていました(1つの場所でそれらを定義する)が、進化したデザインでは各メソッドが独自のインクルードを定義し、それをクラスレベルの変数ではなくパラメータとしてベースリポジトリに送ります。私が上にリンクしたLadislavからの答えはその要点をカバーしています。 – sliderhouserules

0

ない可能性あなたはクラスが完全に汎用的にする場合を除き,:

public class BaseRepo<S> 
{ 
    protected virtual DbSet<S> DefaultInclude(DbSet<S> set) {return set;} 
} 

public class ProductRepo : BaseRepo<Product> 
{ 
    protected override DbSet<Product> DefaultInclude(DbSet<Product> set) 
    { 
     return set.Include("..."); 
    } 
} 
+0

私は上記とは異なるソリューションを探しています。リポジトリを完全に汎用化することはできません。私は以前の化身のリポジトリでは完全に汎用リポジトリを持っていたため、さまざまな理由で非汎用的な方向に進む必要がありました。さらに、エンティティにIncludesを設定する機能が必要です。私のリポジトリの設計は、多くの人々が行っている半典型的な単一リポジトリごとのものではありません。しかし、答えをありがとう。それは有り難いです。 – sliderhouserules

+0

あなたの現在のデザインでは、あなたが求めていることは単にできないとしか言​​えません。あなたは全体の概念を再考したいかもしれません。 – jeroenh

関連する問題