Зарегистрируйте все обработчики автоматически, как показано в этом вопрос / ответ :
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof (IEventHandler<>)).AsImplementedInterfaces();
Если у вас есть TEvent и вы хотите найти все обработчики, получите их, создав конкретный тип интерфейса следующим образом :
Type generic = typeof(IEnumerable<IEventHandler<>>);
Type[] typeArgs = { typeof(TEvent) }; // you might get the Type of TEvent in a different way
Type constructed = generic.MakeGenericType(typeArgs);
Вы должны кэшировать это в словаре, чтобы избежать отражения при каждой отправке события.
Как только вы создадите конкретный тип интерфейса, вы можете спросите у Autofa c обо всех реализациях этого интерфейса:
var handlers = container.Resolve(constructed);
Теперь проблема в том, что с экземплярами обработчика вы можете вызывать метод Handle только с помощью Invoke (отражение). Это проблема производительности, но она не связана с тем, как вы регистрируете и разрешаете обработчики. Это связано с тем, что вам нужно вызывать конкретный метод из объекта. Чтобы избежать использования отражения, вам нужен скомпилированный код, который вызывает конкретный метод (обратите внимание, что использование Generics также создает конкретный скомпилированный код для каждого типа).
Я могу придумать два способа получения скомпилированного кода для выполнения этих вызовов:
Ручная запись делегата, который преобразует экземпляр вашего обработчика объекта в конкретный тип для каждого TEvent. типа что у тебя есть. Затем сохраните все эти делегаты в словаре, чтобы вы могли найти их во время выполнения на основе типа TEvent и вызвать его, передав экземпляр обработчика и экземпляр события. При таком подходе для каждого нового TEvent, который вы создаете, вам нужно создать соответствующий делегат.
Делать то же самое, что и раньше, но с использованием кода при запуске. То же самое, но все создание делегатов - это автомат c.
Обновление
На основе репозитория, совместно используемого OP, я создал рабочую версию. Основной код для разрешения обработчиков и их вызова находится в EventPublisherService
классе
public class EventPublisherService : IEventPublisherService
{
private readonly ILifetimeScope _lifetimeScope;
public EventPublisherService(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public async Task Emit(IEvent eventItem)
{
Type[] typeArgs = { eventItem.GetType() };
Type handlerType = typeof(IEventHandler<>).MakeGenericType(typeArgs);
Type handlerCollectionType = typeof(IEnumerable<>).MakeGenericType(handlerType);
var handlers = (IEnumerable<object>)_lifetimeScope.Resolve(handlerCollectionType);
var handleMethod = handlerType.GetMethod("Handle");
foreach (object handler in handlers)
{
await ((Task)handleMethod.Invoke(handler, new object[] { eventItem }));
}
}
}
Обратите внимание, что, как указано в исходном ответе, это решение не включает в себя очень необходимые оптимизации производительности. Минимум, который нужно сделать, - это кэшировать все типы и MethodInfos, чтобы их не нужно было каждый раз создавать. Как было объяснено, вторая оптимизация заключается в том, чтобы избежать использования Invoke, но ее сложнее достичь, и для IMO требуется отдельный вопрос.