Динамически вызывать метод для общей цели
У меня общий интерфейсICommandHandler<>
который будет иметь несколько реализаций, каждая для обработки конкретной реализацииICommand
Например:
public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
Когда мне даютICommand
объект, который я пытаюсь отправить его динамически к правильномуICommandHandler
, На данный момент я использовал довольно простой подход отражения сInvoke
в моем классе диспетчера:
public void Dispatch<T>(T command) where T : ICommand
{
Type commandType = command.GetType();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
object handler = IoC.Get(handlerType);
MethodInfo method = handlerType.GetMethod("Handle");
method.Invoke(handler, new object[] { command });
}
Есть 2 проблемы с этим подходом. Во-первых, он использует медленное отражение. Во-вторых, если метод выдает какое-либо исключение, он будет помещен вTargetInvocationException
и я потеряю трассировку стека, если перезапущу ее.
Я разработал способ сделать вызов, создав делегата и используяDynamicInvoke
но это не решает проблему с исключениями (и я не уверен,DynamicInvoke
действительно лучше, чемInvoke
):
public void Dispatch<T>(T command) where T : ICommand
{
Type commandType = command.GetType();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
object handler = IoC.Get(handlerType);
MethodInfo method = handlerType.GetMethod("Handle");
Type actionType = typeof(Action<>).MakeGenericType(commandType);
Delegate action = Delegate.CreateDelegate(actionType, handler, method);
action.DynamicInvoke(command);
}
У меня вопрос, есть ли лучший способ добиться того, что я пытаюсь сделать? Желательно, чтобы я мог сделать строго типизированный звонок вместо полученияobject
и глядя вверхMethodInfo
, Я предполагаю, что это невозможно, потому что тип не известен во время компиляции.
Если это невозможно, то следующим решением будет эффективное решение, которое будет генерировать исключение «изначально».
редактировать: Обновлены примеры кода, чтобы уточнить, что я использую IoC (Ninject) для созданияICommandHandler
во время выполнения, а неActivator.CreateInstance()
как я впервые поставил. Включен пример того, как это будет использоваться по запросу:
var command = new CreateUserCommand() { Name = "Adam Rodger" };
var dispatcher = new CommandDispatcher();
dispatcher.Dispatch(command);
// this would send the message to CreateUserCommandHandler.Handle(command)
// dynamically and any exceptions would come back 'natively'
Редактировать 2: Как предлагается ниже, я не могу разыграть результатIoC.Get(handlerType)
вICommandHandler<T>
потому что я получаюInvalidCastException
во время выполнения. Это потому, что во время выполненияT
на самом делеICommand
Я предполагаю, потому что командные классы прибывают через WCF и каким-то образом теряют свою строгую типизацию. Код, который вызывает диспетчер, выглядит примерно так:
[ServiceContract]
public class CommandService
{
[OperationContract]
public void Execute(ICommand command) // no type information
{
var dispatcher = new CommandDispatcher(); // injected by IoC in real version
dispatcher.Dispatch(command);
}
}