web-dev-qa-db-fra.com

Autofac avec plusieurs implémentations de la même interface

J'utilise Autofac et j'aimerais avoir plusieurs implémentations d'une interface. Comment puis-je configurer Autofac pour résoudre les dépendances en fonction du type actuel?

Plus précisément, j'ai une interface et plusieurs implémentations qui doivent être chaînées.

Laissez-moi vous expliquer (classes fictives):

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

Au bas de la chaîne, il pourrait y avoir une IMessageHandler qui ne transmet pas le message à la suivante.

Si je veux la chaîne suivante:

TopLevelClass -> LoggingMessageHandler -> DoSomethingMessageHandler -> FinalHandler

Comment puis-je dire à Autofac 

  • passer LoggingMessageHandler à TopLevelClass (pour remplir sa dépendance de IMessageHandler)
  • passer DoSomethingMessageHandler à LoggingMessageHandler (pour remplir sa dépendance de IMessageHandler)
  • passer LoggingMessageHandler à FinalHandler (pour remplir sa dépendance de IMessageHandler)

Est-ce même possible (j'ai lu sur le support implicite pour IEnumerable )? Ou devrais-je utiliser une classe supplémentaire entre deux (une usine ou quelque chose)?

20
Peter

Autofac a Décorateurs support.

6
Jim Bolla

Pour ceux qui cherchent, je viens de tomber sur cela. Vous pouvez utiliser un support implicite pour IEnumerable. Je l'ai écrit pour une utilisation future .

Fondamentalement, vous pouvez enregistrer les types d'assembly par nom (ou d'autres critères) comme un IEnumerable pouvant être consommés ultérieurement. Ma partie préférée de cette approche est que vous pouvez continuer à ajouter des gestionnaires de messages et tant que vous respectez les mêmes critères, vous ne devez jamais toucher aux critères par la suite.

Enregistrement Autofac:

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

Classe de consommation:

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)
    }
  }
}
17
Kevin R.

Pas trop difficile. Vous pouvez enregistrer des types concrets en tant que soi et les résoudre au fur et à mesure. Ensuite, votre gestionnaire de messages de niveau supérieur (LoggingMessageHandler dans votre exemple) peut être enregistré pour l'interface, qui sera utilisée par votre TopLevelClass

Voici ce que vous regardez (en supposant que vous avez un constructeur par défaut pour 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();
0
arviman