Autofac 过滤器解析了 IEnumerable 实现

Posted

技术标签:

【中文标题】Autofac 过滤器解析了 IEnumerable 实现【英文标题】:Autofac filter resolved IEnumerable implementations 【发布时间】:2018-05-09 08:48:45 【问题描述】:

在autofac中解析IEnumerable时是否可以过滤依赖关系?

我有多个接口实现(在下面的示例中为IHandler),这些实现是在独立项目中使用 Autofac 模块定义和注册的。我希望能够在父类型中解析时过滤实现(在下面的示例中为Processor)。

IHandler 实现可以注入Processor 并在ctor 中过滤,但这需要解析所有实现,无论是否需要它们,这是浪费。

public interface IHandler  
public class Handler1 : IHandler  
public class Handler2 : IHandler  
public class Handler3 : IHandler  

public class Processor 
    public IEnumerable<IHandler> Handlers;

    public Processor(IEnumerable<IHandler> handlers) 
        Handlers = handlers;
    


static void Main(string[] args)

    var builder = new ContainerBuilder();

    builder.RegisterType<Handler1>().As<IHandler>();
    builder.RegisterType<Handler2>().As<IHandler>();
    builder.RegisterType<Handler3>().As<IHandler>();

    builder.RegisterType<Processor>().AsSelf();

    var container = builder.Build();

    var processor = container.Resolve<Processor>();

由于一次只能解析 1 个密钥,因此我使用密钥的尝试无效:

[Flags]
public enum HandlerType

    One = 1,
    Two = 2,
    Three = 4


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(ResolvedParameter.ForKeyed<IEnumerable<IHandler>>(enabledHandlers));

【问题讨论】:

也许这有帮助:autofaccn.readthedocs.io/en/latest/faq/select-by-context.html 您希望根据哪些条件过滤这些处理程序? 感谢@UmutOzel,我也许可以使用ResolvedParameter 使其工作。 @Steven 我应该从配置文件中说得更清楚,例如&lt;add key="Handlers" value="1,3" /&gt;... 【参考方案1】:

我建议使用Meta&lt;T&gt;Lazy&lt;T&gt; implicit relationship types 允许处理器在运行时读取配置并控制所有这些。这也将允许您在不同的条件下进行不同的过滤,或者完全关闭过滤,而无需更改任何键。

使用元数据注册处理程序,而不是作为键控服务...

builder.RegisterType<Handler1>()
       .As<IHandler>()
       .WithMetadata("type", HandlerType.One);
builder.RegisterType<Handler2>()
       .As<IHandler>()
       .WithMetadata("type", HandlerType.Two);
builder.RegisterType<Handler3>()
       .As<IHandler>()
       .WithMetadata("type", HandlerType.Three);

更新您的处理器以采用IEnumerable&lt;Meta&lt;Lazy&lt;IHandler&gt;&gt;&gt; 并在构造期间或稍后需要处理程序时进行过滤,您的电话。

public class Processor

  private readonly IHandler[] _handlers;
  public Processor(IEnumerable<Meta<Lazy<IHandler>>> handlers)
  
    this._handlers =
      handlers
        .Where(h => h.Metadata["type"] == HandlerType.One || h.Metadata["type"] == HandlerType.Three)
        .Select(h => h.Value.Value)
        .ToArray();
  

进入构造函数的每个项目都是Meta&lt;Lazy&lt;IHandler&gt;&gt;

Meta&lt;T&gt; 有一个要查询的 Metadata 字典,Value 将是 Lazy&lt;IHandler&gt;。 在您调用 Value 属性之前,Lazy&lt;T&gt; 不会解析/构造处理程序,因此它不会很昂贵或导致您不想要的解决方案。

所以item.Value.Value 将是已解析的IHandler(如您在上面的LINQ 中所见)。

Where 过滤器中的内容可以基于配置或其他任何内容。但真正的胜利在于,如果您仍然需要在其他地方解析所有处理程序...

public OtherHandlerConsumer(IEnumerable<IHandler> handlers)

...这仍然有效。如果它们都被键控,你就不能这样做。此外,您可以根据需要向注册添加尽可能多的元数据,甚至可以定义更强大的strongly typed metadata,以便您可以更明智地决定所需的处理程序。

【讨论】:

非常有趣的方法,我没有考虑使用Lazy 来减轻解决可能不使用的依赖项的成本。【参考方案2】:

如果您可以在容器设置阶段提供适当的过滤器(就像您在示例代码中所做的那样),您可以将您的 Processor 类型注册更改为以下内容:

builder.RegisterType<Processor>().AsSelf()
    .WithParameter((p, c) => p.Name == "handlers",
        (p, c) => new[] 
            
                c.ResolveKeyed<IHandler>(HandlerType.One),
                c.ResolveKeyed<IHandler>(HandlerType.Three)
            );

您需要使用的值可以从您想要的任何地方获取,也可以从配置文件中获取。

【讨论】:

这与我使用的解决方案很接近,但会根据配置过滤已解析的类...谢谢【参考方案3】:

由于注册的处理程序基于配置开关,这意味着选定的处理程序不会在运行时更改。这与根据一些 runtime 条件选择处理程序非常不同。因此,我提出了类似的建议:

var handlerIds = ConfigurationManager.AppSettings["Handlers"].Split(',');

if (handlerIds.Contains("1")) builder.RegisterType<Handler1>().As<IHandler>();
if (handlerIds.Contains("2")) builder.RegisterType<Handler2>().As<IHandler>();
if (handlerIds.Contains("3")) builder.RegisterType<Handler3>().As<IHandler>();
if (handlerIds.Contains("4")) builder.RegisterType<Handler4>().As<IHandler>();
// etc

builder.RegisterType<Processor>().AsSelf();

【讨论】:

这可能对某些人有用,但在我的情况下,handlers 是“在独立项目中使用 Autofac 模块定义和注册的”,这意味着它们独立于配置等。 我很困惑。在之前的评论中,您描述了它们来自配置。 是的,在示例中,IHandler 使用的配置是一个配置文件。但是每个IHandler impl 都是一个独立的项目,具有最少的依赖和自注册 AutofacModule。抱歉,我应该更清楚一点。 有了这个解释,这并没有真正改变我的答案,除了每个 if 检查操作系统都移到了一个模块中。你仍然不需要键控注册,惰性或元。

以上是关于Autofac 过滤器解析了 IEnumerable 实现的主要内容,如果未能解决你的问题,请参考以下文章

Autofac - ASP.NET Core 中的动作过滤器中的属性注入

一文学会Autofac的基础操作:几种实现注册方式3种注入方式生命周期AOP以及过滤器实现依赖注入...

C# - Autofac解析不同类中的新实例

IoC之AutoFac——解析服务

[翻译]Autofac 解析服务

使用 Autofac 和 IHostBuilder 解析服务