在Autofac中解析IEnumerable时如何过滤依赖项?
如何在Autofac中解析
IEnumerable<IHandler>时过滤依赖项 当然可以解决这个问题!你遇到的痛点很典型——直接把所有IHandler实现注入Processor再过滤,会导致不管用不用得上,所有Handler都被实例化,白白浪费资源。下面给你几种Autofac原生支持的优雅方案,既能实现过滤,又能避免不必要的实例化:
方案1:通过Keyed注册+自定义参数解析
这个方案基于你尝试的Keyed注册思路,改进参数解析逻辑,一次性组合多个Keyed服务为IEnumerable<IHandler>:
[Flags] public enum HandlerType { One = 1, Two = 2, Three = 4 } // 第一步:用Keyed方式注册所有Handler builder.RegisterType<Handler1>().Keyed<IHandler>(HandlerType.One); builder.RegisterType<Handler2>().Keyed<IHandler>(HandlerType.Two); builder.RegisterType<Handler3>().Keyed<IHandler>(HandlerType.Three); // 第二步:定义需要启用的Handler类型 var enabledHandlers = HandlerType.One | HandlerType.Three; // 第三步:注册Processor时,手动解析并组合符合条件的Handler builder.RegisterType<Processor>().AsSelf() .WithParameter( // 匹配构造函数中的IEnumerable<IHandler>参数 (parameter, context) => parameter.ParameterType == typeof(IEnumerable<IHandler>), // 只解析启用的Keyed服务,未选中的不会被实例化 (parameter, context) => { var selectedHandlers = new List<IHandler>(); if ((enabledHandlers & HandlerType.One) != 0) selectedHandlers.Add(context.ResolveKeyed<IHandler>(HandlerType.One)); if ((enabledHandlers & HandlerType.Three) != 0) selectedHandlers.Add(context.ResolveKeyed<IHandler>(HandlerType.Three)); return selectedHandlers; } );
优点:逻辑直接,贴合你最初的Keyed注册思路;只有被选中的Handler会被实例化,资源零浪费。
缺点:如果Handler类型较多,判断逻辑会稍显繁琐。
方案2:使用元数据(Metadata)标记+过滤
通过给Handler注册时添加元数据,再在解析时基于元数据过滤,这种方式扩展性更强,不需要依赖Keyed:
[Flags] public enum HandlerType { One = 1, Two = 2, Three = 4 } // 第一步:注册Handler时添加元数据标记 builder.RegisterType<Handler1>().As<IHandler>().WithMetadata("HandlerType", HandlerType.One); builder.RegisterType<Handler2>().As<IHandler>().WithMetadata("HandlerType", HandlerType.Two); builder.RegisterType<Handler3>().As<IHandler>().WithMetadata("HandlerType", HandlerType.Three); // 第二步:定义需要启用的Handler类型 var enabledHandlers = HandlerType.One | HandlerType.Three; // 第三步:注册Processor时,基于元数据过滤 builder.RegisterType<Processor>().AsSelf() .WithParameter( (parameter, context) => parameter.ParameterType == typeof(IEnumerable<IHandler>), (parameter, context) => { // 解析带元数据的Handler集合(此时Handler实例尚未被创建) var allHandlersWithMeta = context.Resolve<IEnumerable<Meta<IHandler>>>(); // 过滤出符合条件的Handler,并仅实例化这些Handler return allHandlersWithMeta .Where(meta => (enabledHandlers & (HandlerType)meta.Metadata["HandlerType"]) != 0) .Select(meta => meta.Value); } );
优点:元数据可以携带更多额外信息(比如优先级、描述等),扩展性强;过滤逻辑更简洁。
注意:Meta<IHandler>是Autofac提供的包装类型,只有当你访问Value属性时,才会实例化对应的Handler,完全避免资源浪费。
方案3:工厂模式解耦
如果你的过滤条件需要动态调整,或者不想在注册逻辑中硬编码过滤规则,可以用工厂模式解耦:
[Flags] public enum HandlerType { One = 1, Two = 2, Three = 4 } // 定义Handler工厂接口 public interface IHandlerFactory { IEnumerable<IHandler> GetEnabledHandlers(HandlerType enabledTypes); } // 实现工厂,利用Autofac的IIndex安全获取Keyed服务 public class HandlerFactory : IHandlerFactory { private readonly IIndex<HandlerType, IHandler> _handlerIndex; public HandlerFactory(IIndex<HandlerType, IHandler> handlerIndex) { _handlerIndex = handlerIndex; } public IEnumerable<IHandler> GetEnabledHandlers(HandlerType enabledTypes) { var handlers = new List<IHandler>(); // 用TryGetValue避免Key不存在时抛出异常 if ((enabledTypes & HandlerType.One) != 0 && _handlerIndex.TryGetValue(HandlerType.One, out var handler1)) handlers.Add(handler1); if ((enabledTypes & HandlerType.Three) != 0 && _handlerIndex.TryGetValue(HandlerType.Three, out var handler3)) handlers.Add(handler3); return handlers; } } // 注册逻辑 builder.RegisterType<HandlerFactory>().As<IHandlerFactory>(); builder.RegisterType<Handler1>().Keyed<IHandler>(HandlerType.One); builder.RegisterType<Handler2>().Keyed<IHandler>(HandlerType.Two); builder.RegisterType<Handler3>().Keyed<IHandler>(HandlerType.Three); var enabledHandlers = HandlerType.One | HandlerType.Three; builder.RegisterType<Processor>().AsSelf() .WithParameter( (parameter, context) => parameter.ParameterType == typeof(IEnumerable<IHandler>), (parameter, context) => context.Resolve<IHandlerFactory>().GetEnabledHandlers(enabledHandlers) );
优点:过滤逻辑封装在工厂中,注册代码更简洁;可以动态修改启用的Handler类型,甚至在运行时调整过滤规则。
内容的提问来源于stack exchange,提问作者Anth12
相关产品推荐
相关产品推荐

