Bind generic type (scanning) to concrete instance with generic method call

时间:2017-06-15 09:32:28

标签: c# asp.net-core structuremap

Using StructureMap I want to bind IQueryable<T> to ApplicationDbContext.Set<T>().AsQueryable().

So I can inject like this.

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

I've gotten this far

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

But I can't figure out how to get the TNotWorking working :) I guess reflection isn't an option here, also I would avoid it if possible.

1 个答案:

答案 0 :(得分:1)

正如评论中所指出的那样,不可能在项目中反映或实施每个实体作为一种类型。

最后一种方法可能如下所示。当您创建实现IQueryable<T>的基类时,添加新实体所需的代码很少,并且当有多个DbContext可用于注入时也可以工作,这几乎总是如此在任何非平凡的演示应用程序中。

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) { }
}

这样做的另一个好处是,您可以按实体更改基础查询/持久性提供程序,方法是使用QueryableEntityBase<T>更改MonogoDbQueryableEntityBase<T>基础,IMongoClient使用DbContext代替config.For(typeof(IQueryable<>)).Use(typeof(QueryableEntityBase<>));

注册应该是这样的(不熟悉StructureMap,你需要注册它的所有类型或扫描程序集)。

QueryableEntity<T>

在一个简单的情况下,你只有一个数据库,你也可以使基类非抽象,只需解析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(); } } ,但就像我说你会遇到每个应用程序一个DbContext的限制迟早,所以最好明确地完成。

或者扩展实现,以便您也可以定义上下文

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; } ... } 及其子类型,所以它不是很优雅,你也可以使用数据库特定的接口,比如

import tensorflow as tf

def main(_):
    saver = tf.train.Saver()
    with tf.Session() as sess:
        ckpt = tf.train.latest_checkpoint("/data/lstm_models")
        saver.restore(sess, ckpt)

if __name__ == "__main__":
  tf.app.run()