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
LoggingMessageHandler
à TopLevelClass
(pour remplir sa dépendance de IMessageHandler
)DoSomethingMessageHandler
à LoggingMessageHandler
(pour remplir sa dépendance de IMessageHandler
)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)?
Autofac a Décorateurs support.
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.
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)
}
}
}
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();