【问题标题】:Autofac filter resolved IEnumerable implementationsAutofac 过滤器解析了 IEnumerable 实现
【发布时间】: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));

【问题讨论】:

  • 您希望根据哪些条件过滤这些处理程序?
  • 感谢@UmutOzel,我也许可以使用ResolvedParameter 使其工作。
  • @Steven 我应该从配置文件中说得更清楚,例如&lt;add key="Handlers" value="1,3" /&gt;...

标签: c# .net dependency-injection autofac


【解决方案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 检查操作系统都移到了一个模块中。你仍然不需要键控注册,惰性或元。
猜你喜欢
  • 2012-06-19
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
  • 2013-12-20
  • 2016-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多