Question

I'm using Autofac and would like to have multiple implementations of an interface. How can I configure Autofac so to resolve dependencies based on the current type?

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.

If I want the following chain:

TopLevelClass -> LoggingMessageHandler -> DoSomethingMessageHandler -> FinalHandler

How can I tell Autofac to

  • pass LoggingMessageHandler to TopLevelClass (to fulfill its dependency on IMessageHandler)
  • pass DoSomethingMessageHandler to LoggingMessageHandler (to fulfill its dependency on IMessageHandler)
  • pass LoggingMessageHandler to FinalHandler (to fulfill its dependency on IMessageHandler)

Is it even possible (I have read about the implicit support for IEnumerable)? Or will I have to use an extra class in between (a factory or something)?

Was it helpful?

Solution 3

4 options here: https://autofaccn.readthedocs.io/en/latest/faq/select-by-context.html

Option 1: Redesign Your Interfaces

ILoggingMessageHandler , IDoSomethingMessageHandler 

Option 2: Change the Registrations

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

Option 3: Use Keyed Services

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

Option 4: Use Metadata

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

OTHER TIPS

Autofac implicitly supports this by default via the use of IEnumerable<T>. Instead of having your depending class's constructor take in a single instance of T, you make it take in an instance of IEnumerable<T> that will contain every T registered:

public interface IMessageHandler
{
    void HandleMessage(Message m);
}

public class MessageProcessor
{
  private IEnumerable<IMessageHandler> _handlers;

  public MessageProcessor(IEnumerable<IMessageHandler> handlers)
  {
      _handlers = handlers;
  }

  public void ProcessMessage(Message m)
  {
      foreach (var handler in _handlers)
      {
          handler.HandleMessage(m);
      }
  }
}

Then in your registration, simply add multiple implementations of T:

var builder = new ContainerBuilder();
builder.RegisterType<FirstHandler>().As<IMessageHandler>();
builder.RegisterType<SecondHandler>().As<IMessageHandler>();
builder.RegisterType<ThirdHandler>().As<IMessageHandler>();
builder.RegisterType<MessageProcessor>();

When MessageProcessor is instantiated, the IEnumerable it receives will contain three items as per the above registrations against IMessageHandler.

You can read more about this on my blog.

Autofac has Decorators support.

Not too difficult. You can register concrete types as self and resolve it as you go along. Then your top level message handler (LoggingMessageHandler in your example) can be registered for the interface, which will be used by your TopLevelClass

Here's what you're looking at (assuming you have a default constructor for 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();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top