【问题标题】:Bind generic type (scanning) to concrete instance with generic method call使用泛型方法调用将泛型类型(扫描)绑定到具体实例
【发布时间】:2017-06-15 09:32:27
【问题描述】:

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

【问题讨论】:

  • 你不能在不知道具体类型的情况下在编译时进行反射,因为Set&lt;T&gt; 需要在编译时知道 T 并且它Set(typeof(T)) 没有过载。为什么不为您的 dbcontext 和单个 .Set&lt;T&gt; 方法编写一个抽象类?也你的设计作为一些缺陷。当有多个上下文时,如果没有为每个实体创建一个实现作为其自己的可查询对象,您将无法知道它来自哪个上下文
  • 我认为最重要的部分是“为什么要注入IQueryable&lt;Entity&gt;?”看起来设计不正确!

标签: c# asp.net-core structuremap


【解决方案1】:

正如 cmets 中所指出的,没有反射或将每个实体实现为项目中的类型是不可能的。

最后一种方法可能如下所示。当您创建实现IQueryable&lt;T&gt; 的基类时,添加新实体所需的代码很少,并且当有多个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&lt;T&gt; 基础更改为使用IMongoClient 而不是DbContextMonogoDbQueryableEntityBase&lt;T&gt;,来按实体更改基础查询/持久性提供程序。

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

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

在您只有一个数据库的简单情况下,您还可以使基类成​​为非抽象类并仅解析 QueryableEntity&lt;T&gt;,但就像我说的那样,您很快就会遇到每个应用程序一个 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&lt;TContext, TContext&gt; entity 注入您的服务。

但是你失去了拥有持久性无关域的能力,因为你需要能够引用 DbContext 及其子类型,所以它不是很优雅,你也可以使用数据库特定的接口,例如

public interface ISomethingDatabase
{
    IQueryable<User> Users { get; }
    IQueryable<Customer> Customers { get; }
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 2015-12-28
    • 2012-01-11
    • 2011-05-18
    • 2013-11-27
    相关资源
    最近更新 更多