在autofac中间件中检查构造函数参数

如何解决在autofac中间件中检查构造函数参数

我有以下情况:

public interface IService
{
    string Name { get; }
}

public class A : IService
{
    public string Name => "A";
}

public class B : IService
{
    public string Name => "B";
}

public class C
{
    public C(IService first,IService second)
    {
        Console.WriteLine(first.Name);
        Console.WriteLine(second.Name);
    }

    public C(IService single)
    {
        Console.WriteLine(single.Name);
    }
}

我正在使用 Autofac 作为我的DI容器。我想要的是让Autofac在具有相同服务类型的多个参数的情况下使用参数名称作为键来解析类C的依赖项,但仅在这种情况下 (示例中的single参数应正常解析)。

所以最终目标是要具有以下行为:

var builder = new ContainerBuilder();

builder.RegisterType<A>().Keyed<IService>("first");
builder.RegisterType<B>().Keyed<IService>("second");
builder.RegisterType<C>().AsSelf();

// possibly modify the container behavior here

C test = builder.Build().Resolve<C>();

// output:
// A
// B

在我的用例中,不能对每次注册中的行为进行硬编码(例如使用RegistrationExtensions.WithParameter)。 使用Autofac的middleware feature,我仅设法覆盖了所有参数的解析行为(无论构造函数中是否多次使用了参数类型)。 这是我到目前为止所拥有的:

public class ParameterNameAsKeyMiddleware : IResolveMiddleware
{
    public PipelinePhase Phase => PipelinePhase.ParameterSelection;

    public void Execute(ResolveRequestContext context,Action<ResolveRequestContext> next)
    {
        Parameter parameterKeyedByName = new ResolvedParameter(
            (p,i) => true,(p,i) =>
            {
                if (i.TryResolveKeyed(p.Name,p.ParameterType,out object instance))
                {
                    return instance;
                }
                return i.Resolve(p.ParameterType);
            }
        );
        var parameters = context.Parameters.Union(new[] { parameterKeyedByName });

        context.ChangeParameters(parameters);

        // Continue the resolve.
        next(context);
    }
}

并且我在构建容器之前添加了以下代码

// Add custom middleware to every registration.
builder.ComponentRegistryBuilder.Registered += (sender,args) =>
{
    args.ComponentRegistration.PipelineBuilding += (sender2,pipeline) =>
    {
        pipeline.Use(new ParameterNameAsKeyMiddleware());
    };
};

任何帮助将不胜感激。


更新: 感谢Alistair的回答,这是一个可行的解决方案:

public class ParameterNameAsKeyMiddleware : IResolveMiddleware
{
    private static readonly ConcurrentDictionary<ConstructorInfo,IEnumerable<IGrouping<Type,ParameterInfo>>>
        duplicationCache = new ConcurrentDictionary<ConstructorInfo,ParameterInfo>>>();

    public PipelinePhase Phase => PipelinePhase.ParameterSelection;

    public void Execute(ResolveRequestContext context,Action<ResolveRequestContext> next)
    {
        Parameter parameterKeyedByName = new ResolvedParameter(
            DuplicatedServicePredicate,(pi,ctx) => ctx.ResolveKeyed(pi.Name,pi.ParameterType));
        var parameters = context.Parameters.Union(new[] { parameterKeyedByName });
        context.ChangeParameters(parameters);
        // Continue the resolve.
        next(context);

        #region Local functions,called only from the context of this method
        bool DuplicatedServicePredicate(ParameterInfo p,IComponentContext i)
        {
            var constructor = p.Member as ConstructorInfo;
            IEnumerable<IGrouping<Type,ParameterInfo>> duplications = GetDuplicateServices(constructor);
            bool isDuplicate = duplications.Any(g => g.Key == p.ParameterType);
            return isDuplicate;

            // gets groupings of multiple parameters implementing the same service (type).
            // if present,value is fetched from cache. otherwise value is reflected.
            IEnumerable<IGrouping<Type,ParameterInfo>> GetDuplicateServices(ConstructorInfo constructorInfo)
            {
                if (!duplicationCache.TryGetValue(
                        constructorInfo,out IEnumerable<IGrouping<Type,ParameterInfo>> duplicateServices))
                {
                    duplicateServices = constructorInfo.GetParameters()
                        .GroupBy(pi => pi.ParameterType)
                        .Where(g => g.Count() > 1);

                    duplicationCache.AddOrUpdate(
                        constructorInfo,duplicateServices,(x,y) => throw new InvalidOperationException("values should only be added"));
                }

                return duplicateServices;
            }
        }
        #endregion
    }
}

解决方法

我相信有一种方法可以从MethodInfo跳到ParameterInfo

您需要在当前仅返回true的地方实现条件谓词。

在该谓词中,您应该访问提供的Member ParameterInfo上的p属性,该属性将您带到构造方法(广播到MethodInfo)。从那里,您可以访问参数列表并确定是否存在多个相同类型的参数。如果所有参数都具有唯一类型,则返回false,这样正常的Autofac解析就可以进行。

您可能想考虑将检查结果缓存在构造函数的ConcurrentDictionary上键入的MemberInfo中,因此您不必每次都进行该检查。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?