Предположим, что эти классы/интерфейсы:
public interface ICommand
{
}
public class SomeCommand : ICommand
{
}
public interface ICommandHandler<T> where T : ICommand
{
void Handle(T arg);
}
public class SomeCommandHandler : ICommandHandler<SomeCommand>
{
void Handle(SomeCommand arg){ /* do something */ }
}
public interface ICommandBus
{
void RegisterHandler<T>(T t) where T : ICommandHandler<T>;
void RegisterHandlerByParam<T2>(ICommandHandler<T2> t2) where T2 : ICommand;
void RegisterHandlerMethod<T3>(Action<T3> action) where T3 : ICommand
}
public class TheCommandBus : ICommandBus
{
// implements ICommandBus ...
}
Я хочу зарегистрировать все реализации ICommandHandler‹> автоматически. Все варианты (Register *) являются допустимыми решениями, хотя я бы предпочел параметр Action, поскольку он более гибкий и не зависит от интерфейса Handler (просто делегат действия).
Autofac имеет возможность регистрировать типы на основе сканирования сборки и регистрировать найденные реализации универсального интерфейса, например:
builder.RegisterAssemblyTypes(Assembly.LoadFrom("MyAssembly.dll"))
.AsClosedTypesOf(typeof(ICommandHandler<>));
Итак, у меня зарегистрированы все реализации. Теперь мне нужно автоматически зарегистрировать их всех в TheCommandBus. Как это сделать?
Я могу сделать это вручную, добавив эти строки (например, во время OnActivated):
builder.RegisterType<TheCommandBus>().As<ICommandBus>().OnActivated(args =>
{
// now I need to list all implementations here!!! please, no...
args.Instance.RegisterHandler<ICommandHandler<SomeCommand>>(args.Context.Resolve<ICommandHandler<SomeCommand>>());
// does not look better to me than before ...
args.Instance.RegisterHandlerByParam<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>())
// uses delegate for, but still need to list all variants
args.Instance.RegisterHandlerMethod<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>().Handle)
});
Если я хочу использовать такой тип в лямбда-выражении во время регистрации, у меня есть проблема, что мне нужно определить конкретный тип, как в этом примере в процессе активации для другого компонента. Но я не хочу перечислять их все вручную... хочу что-то подобное автоматически.
Как перехватить все реализации ICommandHandler и автоматически зарегистрировать их с помощью метода Register*?
Изменить:
Другой вариант — расширить класс SomeCommandHandler, чтобы он регистрировал себя при разрешении внутри его конструктора:
public SomeCommandHandler(ICommandBus commandBus)
{
// and register here, for example
commandBus.RegisterHandlerbyParam(this);
}
Таким образом, я должен предоставить AutoActivate() для результата регистрации AsClosedTypesOf. (возможное решение, но теперь у «обработчиков» две обязанности... регистрация и обработка)