2017-06-15 24 views
2

StructureMapを使用するIQueryable<T>ApplicationDbContext.Set<T>().AsQueryable()にバインドします。ジェネリックメソッド呼び出しで具体的なインスタンスにジェネリック型(スキャン)をバインドする

私はこのように注射することができます。

public class Foo 
{ 
    public Foo(IQueryable<MyEntity> query) 
    { 
    } 
} 

私はこれまでのところ

config.For(typeof(IQueryable<>)) 
    .Use(x => 
    x.GetInstance<ApplicationDbContext>().Set<TNotWorking>().AsQueryable()); 

得ている。しかし、私はTNotWorking私は反射がここにオプションではありません推測:)働いて取得する方法を見つけ出すことができない、また、私はそれを避けるだろう可能なら。

+0

'Set 'はコンパイル時にTを知っている必要があり、 'Set()のためのオーバーロードがないので、コンパイル時**の具体的な型を知っていても、タイプ(T)) 'なぜあなたのdbcontextの抽象クラスと単一の '.Set 'メソッドを書くのではないのですか?また、いくつかの欠陥としてあなたのデザイン。複数のコンテキストを持つ場合、各エンティティの実装を独自のクエリ可能なものとすることなく、どのエンティティから実装するかはわかりません。 – Tseng

+0

最も重要な部分は「IQueryable をなぜ注入しますか?間違ったデザインのように見える! –

答えて

1

コメントで指摘されているように、リフレクションまたはプロジェクト内のタイプとして各エンティティを実装することはできません。

最後のアプローチは次のようになります。 IQueryable<T>を実装する基本クラスを作成すると、新しいエンティティの追加に必要なコードが最小限に抑えられ、DbContextが1つ以上注入可能な場合にも機能します。これはほとんどの場合、通常のデモアプリケーションで使用されます。

public abstract class QueryableEntityBase<TEntity> : IQueryable<TEntity> where TEntity : class 
{ 

    protected QueryableEntityBase(DbContext context) 
    { 
     Context = context ?? throw new ArgumentNullException(nameof(context)); 
     Queryable = context.Set<TEntity>().AsQueryable(); 
    } 

    public Type ElementType => Queryable.ElementType; 

    public Expression Expression => Queryable.Expression; 

    public IQueryProvider Provider => Queryable.Provider; 
    protected IQueryable<TEntity> Queryable { get; } 
    private DbContext Context { get; } 

    public IEnumerator<TEntity> GetEnumerator() => Queryable.GetEnumerator(); 
    IEnumerator IEnumerable.GetEnumerator() => Queryable.GetEnumerator(); 
} 

// You need to create this per entity you want to be injectable 
public class MyQueryableEntity : QueryableEntityBase<MyEntity> 
{ 
    public MyQueryableEntity(ApplicationDbContext context) : base(context) { } 
} 

これは、あなたがIMongoClient代わりのDbContextを使用していますMonogoDbQueryableEntityBase<T>QueryableEntityBase<T>ベースを変更することで、エンティティによって根本的なクエリ/永続性プロバイダを変更することができるという追加の利点を有します。

登録は、次の行に沿ったものでなければなりません(StructureMapに精通していない場合は、すべての型を登録したりアセンブリをスキャンする必要があります)。 1つのデータベースのみを持って、あなたはまた、基本クラスは非抽象作るだけQueryableEntity<T>を解決できる単純な場合には

config.For(typeof(IQueryable<>)).Use(typeof(QueryableEntityBase<>)); 

、私はあなたがアプリごとに1つのDbContextの制限を打つだろうと述べたように遅かれ早かれ、明示的に行うのが最善です。

あなたも

public class QueryableEntityBase<TContext, TEntity> : IContextSetQueryable<TDbContext, TEntity>, IQueryable<TEntity> where TEntity : class, TContext : DbContext 
{ 
    private TContext Context { get; } 

    protected QueryableEntityBase(TContext context) 
    { 
     Context = context ?? throw new ArgumentNullException(nameof(context)); 
     Queryable = context.Set<TEntity>().AsQueryable(); 
    } 
} 

をコンテキストを定義することができます。しかし、あなたは追加のインタフェース必要なので、実装を拡張

または代わりに:次に

public interface IContextSetQueryable<TContext, TContext> : IQueryable<TEntity> where TContext : DbContext { } 

あなたのサービスにIContextSetQueryable<TContext, TContext> entityを注入します。

しかし、その後、あなたがDbContextを参照する機能が必要になりますので、あなたは、持続性にとらわれないドメインを持っている能力を失うと、それはサブタイプですので、それは本当にエレガントではありませんし、あなたにも、このような

などのデータベース固有のインタフェースを、使用することができます
public interface ISomethingDatabase 
{ 
    IQueryable<User> Users { get; } 
    IQueryable<Customer> Customers { get; } 
    ... 
} 
関連する問題