使用泛型方法调用将泛型类型(扫描)绑定到具体实例

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

使用 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 工作 :) 我想反射在这里不是一个选项,我也会尽可能避免它。

正如评论中所指出的,不可能 w/o 在您的项目中将每个实体作为一个类型进行反思或实现。

最后一种方法可能如下所示。当您创建一个实现 IQueryable<T> 的基础 class 时,添加新实体所需的代码最少,并且在有多个 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) { }
}

这还有一个额外的好处,即您可以通过实体更改基础 query/persistence 提供程序,方法是将 QueryableEntityBase<T> 基础更改为 MonogoDbQueryableEntityBase<T>,使用 IMongoClient 而不是 DbContext

注册应该符合(不熟悉 StructureMap,尽管您需要注册所有类型或扫描程序集)。

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

在您只有一个数据库的简单情况下,您也可以使基础 class 成为非抽象的并且只解析 QueryableEntity<T>,但是就像我说的那样您会遇到限制每个应用程序迟早会有一个 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; }
    ...
}