Ответ 1
Autofac поддерживает Decorators.
Я использую Autofac и хочу иметь несколько реализаций интерфейса. Как настроить Autofac для разрешения зависимостей на основе текущего типа?
В частности, у меня есть один интерфейс и несколько реализаций, которые должны быть соединены вместе.
Позвольте мне объяснить (фиктивные классы):
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);
}
}
В нижней части цепочки может быть IMessageHandler
, который не передает сообщение на следующую.
Если мне нужна следующая цепочка:
TopLevelClass -> LoggingMessageHandler -> DoSomethingMessageHandler -> FinalHandler
Как я могу указать Autofac на
LoggingMessageHandler
в TopLevelClass
(чтобы выполнить свою зависимость от IMessageHandler
)DoSomethingMessageHandler
в LoggingMessageHandler
(чтобы выполнить свою зависимость от IMessageHandler
)LoggingMessageHandler
в FinalHandler
(чтобы выполнить свою зависимость от IMessageHandler
)Возможно ли это (я читал о неявной поддержке IEnumerable)? Или мне придется использовать дополнительный класс между ними (a factory или что-то еще)?
Autofac поддерживает Decorators.
Для любого другого поиска я просто наткнулся на это. Вы можете использовать неявную поддержку IEnumerable. Я написал его для будущего использования.
В принципе, вы можете регистрировать типы сборок по имени (или другим критериям) как IEnumerable, которые могут быть использованы позже. Моя любимая часть этого подхода заключается в том, что вы можете продолжать добавлять обработчики сообщений, и пока вы придерживаетесь тех же критериев, вам никогда не придется прикоснуться к критериям после этого.
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)
}
}
}
Не слишком сложно. Вы можете регистрировать конкретные типы как себя и разрешать их по мере продвижения. Затем ваш обработчик сообщений верхнего уровня (LoggingMessageHandler в вашем примере) может быть зарегистрирован для интерфейса, который будет использоваться вашим TopLevelClass
Здесь вы смотрите (при условии, что у вас есть конструктор по умолчанию для 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();