Я был вызван этим вопросом SO о (.NET 4.0) ковариационной и контравариантной поддержке Autofac, и теперь я пытаюсь добиться чего-то подобного, но без при удаче.
То, что я пытаюсь достичь, - это настроить Autofac таким образом, что когда я разрешаю один конкретный IEventHandler<TEvent>
(для демонстрации с использованием container.Resolve
, но обычно, конечно, с использованием встраивания конструктора) Autofac вернет мне MultipleDispatchEventHandler<TEvent>
, который обертывает все зарегистрированные обработчики событий, которые можно назначить из запрошенного обработчика.
Другими словами, когда я пишу это:
var handler = container
.GetInstance<IEventHandler<CustomerMovedEvent>>();
handler.Handle(new CustomerMovedEvent());
Что касается дизайна приложения (приведенного ниже), я бы ожидал возвращения MultipleDispatchEventHandler<CustomerMovedEvent>
, которое обертывает как CustomerMovedEventHandler
, так и NotifyStaffWhenCustomerMovedEventHandler
.
Вот дизайн приложения:
// Events:
public class CustomerMovedEvent { }
public class CustomerMovedAbroadEvent : CustomerMovedEvent { }
public class SpecialCustomerMovedEvent : CustomerMovedEvent { }
// Event handler definition (note the 'in' keyword):
public interface IEventHandler<in TEvent>
{
void Handle(TEvent e);
}
// Event handler implementations:
public class CustomerMovedEventHandler
: IEventHandler<CustomerMovedEvent>
{
public void Handle(CustomerMovedEvent e) { ... }
}
public class NotifyStaffWhenCustomerMovedEventHandler
: IEventHandler<CustomerMovedEvent>
{
public void Handle(CustomerMovedEvent e) { ... }
}
public class CustomerMovedAbroadEventHandler
: IEventHandler<CustomerMovedAbroadEvent>
{
public void Handle(CustomerMovedAbroadEvent e) { ... }
}
Это определение MultipleDispatchEventHandler<TEvent>
, определенное в корне композиции:
// A composite wrapping possibly multiple handlers.
public class MultipleDispatchEventHandler<TEvent>
: IEventHandler<TEvent>
{
private IEnumerable<IEventHandler<TEvent>> handlers;
public MultipleDispatchEventHandler(
IEnumerable<IEventHandler<TEvent>> handlers)
{
this.handlers = handlers;
}
public void Handle(TEvent e)
{
this.handlers.ToList().ForEach(h => h.Handle(e));
}
}
Это моя текущая конфигурация:
var builder = new ContainerBuilder();
// Note the use of the ContravariantRegistrationSource (which is
// available in the latest release of Autofac).
builder.RegisterSource(new ContravariantRegistrationSource());
builder.RegisterAssemblyTypes(typeof(IEventHandler<>).Assembly)
.AsClosedTypesOf(typeof(IEventHandler<>));
// UPDATE: I'm registering this last as Kramer suggests.
builder.RegisterGeneric(typeof(MultipleDispatchEventHandler<>))
.As(typeof(IEventHandler<>)).SingleInstance();
var container = builder.Build();
При текущей конфигурации приложение не работает во время вызова Resolve
, со следующим исключением:
Autofac.Core.DependencyResolutionException: круговой компонент обнаружена зависимость: MultipleDispatchEventHandler'1 [[SpecialCustomerMovedEvent]] → IEventHandler'1 [[SpecialCustomerMovedEvent]] [] → MultipleDispatchEventHandler'1 [[SpecialCustomerMovedEvent]].
Теперь, конечно, вопрос: как я могу исправить конфигурацию (или дизайн), чтобы поддержать это?