使用泛型方法调用将泛型类型(扫描)绑定到具体实例
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; }
...
}
使用 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; }
...
}