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;
    }
}