C# 获取泛型服务类型的所有实现-包括开放泛型 背景
我正在编写一些集成测试,测试特定接口的实现,C# 获取泛型服务类型的所有实现-包括开放泛型 背景,c#,generics,simple-injector,C#,Generics,Simple Injector,我正在编写一些集成测试,测试特定接口的实现,IQueryMapping。此接口用于从IQueryable映射到IQueryable。我想确保他们能做到 使用实体框架编译查询时不引发异常 任务 我很懒,我不想每次创建新映射时都要更新我的测试!我所要做的就是确保我的应用程序使用的每个映射都将通过测试。我可以引导容器,然后找到所有注册的实现,如下所示: from r in Container.GetCurrentRegistrations() let t = r.ServiceType where t
IQueryMapping
。此接口用于从IQueryable
映射到IQueryable
。我想确保他们能做到
使用实体框架编译查询时不引发异常
任务
我很懒,我不想每次创建新映射时都要更新我的测试!我所要做的就是确保我的应用程序使用的每个映射都将通过测试。我可以引导容器,然后找到所有注册的实现,如下所示:
from r in Container.GetCurrentRegistrations()
let t = r.ServiceType
where t.IsGenericType && t.GetGenericTypeDefinition() == typeof (IQueryMapping<,>)
select r.GetInstance()
我想让容器告诉我这个专门的
IQueryMapping
,这样我就可以请求它的一个实例,并在测试中运行它。据我所知,没有内置的方法可以做到这一点。然而,当我在写我的问题时(就像经常发生的那样),我找到了一种实现我想做的事情的方法。这可能远不理想,但
从表面上看,这些信息在注册中似乎不容易获得——它只在解析时(在“构建参数”点)计算
拿1
我想到的一个想法是迭代每个注册类型,并检查其构造函数中可能的参数:
from r in container.GetCurrentRegistrations()
from ctor in r.Registration.ImplementationType.GetConstructors()
from param in ctor.GetParameters()
let t = param.ParameterType
where t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IQueryMapping<,>)
select t;
这将在所有注册的类型中递归,通过它们的构造函数查找其他要搜索的类型。然而,这仍然不是完美的,因为我们不能保证一个特定的组件应该通过它的构造函数来解析(与工厂方法相反)
第三次
InstanceProducer
上一个有趣的方法是BuildExpression()
。此方法创建一个表达式,该表达式在执行时将创建给定实例。但是,由于它是一个表达式,因此也可以使用ExpressionVisitor遍历它。我们可以创建ExpressionVisitor的实现,该实现收集表达式中的所有类型:
public static class ContainerExtensions
{
public static IEnumerable<InstanceProducer> GetInstanceProducers(this Container container)
{
return container.GetCurrentRegistrations()
.SelectMany(GetExpressionTypes)
.Distinct()
.Select(container.GetRegistration);
}
private static IEnumerable<Type> GetExpressionTypes(InstanceProducer instanceProducer)
{
var expression = instanceProducer.BuildExpression();
var visitor = new TypeExpressionVisitor();
visitor.Visit(expression);
return visitor.Types;
}
private class TypeExpressionVisitor : ExpressionVisitor
{
private readonly List<Type> _types;
public IEnumerable<Type> Types
{
get { return _types; }
}
public TypeExpressionVisitor()
{
_types = new List<Type>();
}
protected override Expression VisitNew(NewExpression node)
{
_types.Add(node.Type);
return base.VisitNew(node);
}
protected override Expression VisitInvocation(InvocationExpression node)
{
_types.Add(node.Type);
return base.VisitInvocation(node);
}
}
}
我想知道我是做得对还是无意中把自己挖到了一个洞里,所以如果你在这方面有知识,请给我反馈 调用
RegisterOpenEneric
将在后台将代理挂接到ResolveUnregisteredType
事件上。这基本上意味着容器本身完全不知道注册,只有在请求注册抽象的封闭泛型版本时,才会添加注册;可以直接使用对GetInstance()
的调用,也可以间接使用,因为依赖于该抽象的类型已解析
这里的技巧是在调用GetCurrentRegistrations()
之前调用Verify()
。调用Verify()
将导致容器构建所有表达式树,编译所有委托,并创建容器已知的所有注册的所有实例。这将迫使容器添加每个找到的开放泛型抽象的封闭泛型版本的注册
长话短说:首先调用
Verify()
。GetCurrentRegistrations()
还将返回注册为开放泛型类型的类型的所有关闭版本(例如您的IQueryMapping
),但只有在容器知道它存在之后;这基本上意味着你应该解决一个消费者。这实际上意味着在调用Verify()
之后,所有关闭的版本都可以从GetCurrentRegistrations()
获得。长话短说:打电话给Verify()
@Steven-哇,我真不敢相信我错过了(而且花了这么多时间在错误的地方寻找)!如果你能写下来作为回答,我会接受的。这是一个非常彻底的分析。很高兴看到你深入图书馆+1美元。幸运的是,解决方案比您在这里建议的要简单得多:-)@Steven这是一个很棒的库!谢谢你写这封信,非常欢迎。请继续关注v3,该版本将更棒:D
from r in container.GetCurrentRegistrations()
from ctor in r.Registration.ImplementationType.GetConstructors()
from param in ctor.GetParameters()
let t = param.ParameterType
where t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IQueryMapping<,>)
select t;
public static class ContainerExtensions
{
public static IEnumerable<InstanceProducer> GetInstanceProducers(this Container container)
{
return container.GetCurrentRegistrations()
.SelectMany(x => GetInstanceProducers(container, x));
}
private static IEnumerable<InstanceProducer> GetInstanceProducers(Container container, InstanceProducer instanceProducer)
{
yield return instanceProducer;
var producers = from ctor in instanceProducer.Registration
.ImplementationType.GetConstructors()
from param in ctor.GetParameters()
from producer in GetInstanceProducers(
container,
container.GetRegistration(param.ParameterType))
select producer;
foreach (var producer in producers)
yield return producer;
}
}
public static class ContainerExtensions
{
public static IEnumerable<InstanceProducer> GetInstanceProducers(this Container container)
{
return container.GetCurrentRegistrations()
.SelectMany(GetExpressionTypes)
.Distinct()
.Select(container.GetRegistration);
}
private static IEnumerable<Type> GetExpressionTypes(InstanceProducer instanceProducer)
{
var expression = instanceProducer.BuildExpression();
var visitor = new TypeExpressionVisitor();
visitor.Visit(expression);
return visitor.Types;
}
private class TypeExpressionVisitor : ExpressionVisitor
{
private readonly List<Type> _types;
public IEnumerable<Type> Types
{
get { return _types; }
}
public TypeExpressionVisitor()
{
_types = new List<Type>();
}
protected override Expression VisitNew(NewExpression node)
{
_types.Add(node.Type);
return base.VisitNew(node);
}
protected override Expression VisitInvocation(InvocationExpression node)
{
_types.Add(node.Type);
return base.VisitInvocation(node);
}
}
}
public static IEnumerable<object[]> GetMappingObjects
{
get
{
return
from r in Container.GetInstanceProducers()
where IsAssignableToGenericType(r.ServiceType, typeof(IQueryMapping<,>))
select new[] {r.GetInstance()};
}
}
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
while (true)
{
var interfaceTypes = givenType.GetInterfaces();
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
if (interfaceTypes.Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == genericType))
return true;
var baseType = givenType.BaseType;
if (baseType == null)
return false;
givenType = baseType;
}
}