据我所知,没有内置的方法可以做到这一点。然而,在编写我的问题时(经常发生这种情况),我找到了一种实现我想要做的事情的方法。这可能远非理想,但是...
从Simple Injector Pipeline documentation中看来,这些信息在注册时不容易获得——它只在解析时计算(在“Build ctor arguments”时)。
方案一
我想到的一个想法是遍历每个已注册的类型,并检查其构造函数是否有可能的参数:
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;
然而,这只能下降到注册类型的第一层 - 在我的项目中有许多开放式泛型注册。
第二步
幸运的是,Simple Injector提供了一种根据服务类型检索InstanceProducer的方法,这就是我们需要创建递归函数的全部内容。
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;
}
}
这会递归遍历所有已注册的类型,查找它们的构造函数以找到其他要搜索的类型。但是,这仍然不完美,因为我们无法保证特定组件应该通过其构造函数解析(而不是例如工厂方法)。
第三步
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);
}
}
}
终于!由ExpressionVisitor收集到的类型可以传递给container.GetRegistration(t)
。这些类型将是具体类型,因此我们需要对Take 1中的LINQ语句进行小的更改,使用一个方法来测试服务类型是否可分配给任何泛型版本的IQueryMapping<,>
:
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;
}
}
我想知道我是否做得正确,或者无意中让自己陷入了困境,所以如果您在这个领域有知识,请给我反馈!
GetCurrentRegistrations()
方法还会返回所有已关闭的泛型类型注册版本(例如IQueryMapping<,>
),但前提是容器知道它们的存在,这基本上意味着您应该已经解析了一个消费者。这实际上意味着在调用Verify()
后,所有已关闭的版本都可以从GetCurrentRegistrations()
中获取。长话短说:请调用Verify()
。 - Steven