Autofac with multiple implementations of the same interface

42,445

Solution 1

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

Solution 2

For anyone else searching, I just ran across this. You can use implicit support for the 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. 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:

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

Solution 3

Autofac has Decorators support.

Share:
42,445
kingfleur
Author by

kingfleur

I help companies and teams turn legacy code into maintainable software. You can see my work on Twitter, my blog, Careers, LinkedIn, BitBucket and GitHub.

Updated on March 28, 2020

Comments

  • kingfleur
    kingfleur about 4 years

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