Search code examples
c#dependency-injectioninversion-of-controlautofac

Autofac with multiple implementations of the same interface


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


Solution

  • 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) { ... }
    }