Подтвердить что ты не робот

Autofac с несколькими реализациями одного и того же интерфейса

Я использую Autofac и хочу иметь несколько реализаций интерфейса. Как настроить Autofac для разрешения зависимостей на основе текущего типа?

В частности, у меня есть один интерфейс и несколько реализаций, которые должны быть соединены вместе.

Позвольте мне объяснить (фиктивные классы):

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);
    }
}

В нижней части цепочки может быть IMessageHandler, который не передает сообщение на следующую.

Если мне нужна следующая цепочка:

TopLevelClass -> LoggingMessageHandler -> DoSomethingMessageHandler -> FinalHandler

Как я могу указать Autofac на

  • передать LoggingMessageHandler в TopLevelClass (чтобы выполнить свою зависимость от IMessageHandler)
  • перейдите DoSomethingMessageHandler в LoggingMessageHandler (чтобы выполнить свою зависимость от IMessageHandler)
  • передать LoggingMessageHandler в FinalHandler (чтобы выполнить свою зависимость от IMessageHandler)

Возможно ли это (я читал о неявной поддержке IEnumerable)? Или мне придется использовать дополнительный класс между ними (a factory или что-то еще)?

4b9b3361

Ответ 1

Autofac поддерживает Decorators.

Ответ 2

Для любого другого поиска я просто наткнулся на это. Вы можете использовать неявную поддержку IEnumerable. Я написал его для будущего использования.

В принципе, вы можете регистрировать типы сборок по имени (или другим критериям) как IEnumerable, которые могут быть использованы позже. Моя любимая часть этого подхода заключается в том, что вы можете продолжать добавлять обработчики сообщений, и пока вы придерживаетесь тех же критериев, вам никогда не придется прикоснуться к критериям после этого.

Регистрация автозапуска:

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

Класс потребления:

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)
    }
  }
}

Ответ 3

Не слишком сложно. Вы можете регистрировать конкретные типы как себя и разрешать их по мере продвижения. Затем ваш обработчик сообщений верхнего уровня (LoggingMessageHandler в вашем примере) может быть зарегистрирован для интерфейса, который будет использоваться вашим TopLevelClass

Здесь вы смотрите (при условии, что у вас есть конструктор по умолчанию для 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();