简体   繁体   English

Autofac 具有相同接口的多个实现

[英]Autofac with multiple implementations of the same interface

I'm using Autofac and would like to have multiple implementations of an interface.我正在使用 Autofac 并希望有一个接口的多个实现。 How can I configure Autofac so to resolve dependencies based on the current type?如何配置 Autofac 以便根据当前类型解决依赖关系?

More specifically, I have one interface and multiple implementations that should be chained together.更具体地说,我有一个接口和多个应该链接在一起的实现。

Let me explain (fictitious classes):让我解释一下(虚构的课程):

public interface IMessageHandler
{
    void Handle(Message message);
}

public class LoggingMessageHandler : IMessageHandler
{
    private IMessageHandler _messageHandler;

    public LoggingMessageHandler(IMessageHandler messageHandler)
    {
        _messageHandler = messageHandler;
    }

    public void Handle(Message message) 
    {
        // log something
        _messageHandler.Handle(message);
    }
}

public class DoSomethingMessageHandler : IMessageHandler
{
    private IMessageHandler _messageHandler;

    public DoSomethingMessageHandler (IMessageHandler messageHandler)
    {
        _messageHandler = messageHandler;
    }

    public void Handle(Message message) 
    {
        // do something
        _messageHandler.Handle(message);
    }
}

At the bottom of the chain might be an IMessageHandler that doesn't pass the message on to the next one.链的底部可能是一个IMessageHandler ,它不会将消息传递给下一个。

If I want the following chain:如果我想要以下链:

TopLevelClass -> LoggingMessageHandler -> DoSomethingMessageHandler -> FinalHandler

How can I tell Autofac to我如何告诉 Autofac

  • pass LoggingMessageHandler to TopLevelClass (to fulfill its dependency on IMessageHandler )LoggingMessageHandler传递给TopLevelClass (以实现其对IMessageHandler依赖)
  • pass DoSomethingMessageHandler to LoggingMessageHandler (to fulfill its dependency on IMessageHandler )DoSomethingMessageHandler传递给LoggingMessageHandler (以实现其对IMessageHandler依赖)
  • pass LoggingMessageHandler to FinalHandler (to fulfill its dependency on IMessageHandler )LoggingMessageHandler传递给FinalHandler (以实现其对IMessageHandler依赖)

Is it even possible (I have read about the implicit support for IEnumerable )?甚至有可能(我已经阅读了对 IEnumerable隐式支持)? Or will I have to use an extra class in between (a factory or something)?或者我必须在两者之间使用一个额外的类(工厂或其他东西)?

For anyone else searching, I just ran across this.对于其他搜索的人,我只是遇到了这个。 You can use implicit support for the IEnumerable.您可以对 IEnumerable 使用隐式支持。 I wrote it up for future use . 我把它写下来以备将来使用

Basically, you can register assembly types by name (or other criteria) as an IEnumerable which can be consumed later.基本上,您可以按名称(或其他条件)将程序集类型注册为 IEnumerable,稍后可以使用它。 My favorite part of this approach is that you can keep adding message handlers and as long as you stick to the same criteria, you never have to touch the criteria afterwards.我最喜欢这种方法的部分是,您可以不断添加消息处理程序,只要您坚持相同的标准,以后就不必再触及这些标准。

Autofac Registration: Autofac注册:

builder.RegisterAssemblyTypes(typeof (LoggingMessageHandler).Assembly)
  .Where(x => x.Name.EndsWith("MessageHandler"))
  .AsImplementedInterfaces();

Consuming Class:消费类:

public class Foo
{
  private readonly IEnumerable<IMessageHandler> _messageHandlers

  public Foo(IEnumerable<IMessageHandler> messageHandlers)
  {
    _messageHandlers = messageHandlers;
  }

  public void Bar(message)
  {
    foreach(var handler in _messageHandlers)
    {
      handler.Handle(message)
    }
  }
}

Autofac 有装饰器支持。

4 options here: https://autofaccn.readthedocs.io/en/latest/faq/select-by-context.html这里有 4 个选项: https : //autofaccn.readthedocs.io/en/latest/faq/select-by-context.html

Option 1: Redesign Your Interfaces选项 1:重新设计您的界面

ILoggingMessageHandler , IDoSomethingMessageHandler 

Option 2: Change the Registrations选项 2:更改注册

builder.Register(ctx => new FinalHandler(ctx.Resolve<LoggingMessageHandler >()));
or
builder.Register(ctx => new FinalHandler(ctx.Resolve<IDoSomethingMessageHandler >()));

Option 3: Use Keyed Services选项 3:使用密钥服务

builder.RegisterType<FinalHandler>()
           .WithParameter(
             new ResolvedParameter(
               (pi, ctx) => pi.ParameterType == typeof(IMessageHandler),
               (pi, ctx) => ctx.ResolveKeyed<ISender>("something")));

Option 4: Use Metadata选项 4:使用元数据

public class FinalHandler
{
  public FinalHandler([WithMetadata("sendBy", "something")] IMessageHandler messageHandler) { ... }
}

Not too difficult.不是太难。 You can register concrete types as self and resolve it as you go along.您可以将具体类型注册为 self 并在进行时解析它。 Then your top level message handler (LoggingMessageHandler in your example) can be registered for the interface, which will be used by your TopLevelClass然后您的顶级消息处理程序(在您的示例中为 LoggingMessageHandler)可以为接口注册,它将被您的 TopLevelClass 使用

Here's what you're looking at (assuming you have a default constructor for FinalHandler)这就是你所看到的(假设你有一个 FinalHandler 的默认构造函数)

var builder = new ContainerBuilder();
builder.RegisterType<FinalHandler>().AsSelf().SingleInstance();
builder.Register(c => new DoSomethingMessageHandler(c.Resolve<FinalHandler>())).AsSelf().SingleInstance();
builder.Register(c => new LoggingMessageHandler(c.Resolve<DoSomethingMessageHandler>())).As<IMessageHandler>().SingleInstance();
//now finally your top level class - this will automatically pick your LoggingMessageHandler since the others have been registered onto their concreteTypes only
builder.RegisterType<TopLevelClass>().As<ITopLevelClass>().InstancePerOwned();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM