Chamar dinamicamente um método em um destino genérico
Eu tenho uma interface genéricaICommandHandler<>
que terá um número de implementações cada para processar uma implementação específica deICommand
, por exemplo.:
public class CreateUserCommand : ICommand { ... }
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand> { ... }
Quando eu sou dado umICommand
objeto estou tentando enviá-lo dinamicamente para o corretoICommandHandler
. No momento eu usei uma abordagem de reflexão bastante direta com umInvoke
na minha classe dispatcher:
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 });
}
Existem 2 problemas com essa abordagem. Em primeiro lugar, usa reflexão lenta. Em segundo lugar, se o método lançar algum tipo de exceção, então ele será envolvido em umTargetInvocationException
e eu vou perder o rastreio da pilha se eu voltar a jogá-lo.
Eu trabalhei uma maneira de fazer a chamada, criando um delegado e usandoDynamicInvoke
mas isso não resolve o problema com exceções (e eu não tenho certezaDynamicInvoke
é realmente melhor do queInvoke
):
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);
}
Minha pergunta é, existe uma maneira melhor de conseguir o que estou tentando fazer? De preferência, eu poderia fazer uma chamada fortemente tipificada em vez deobject
e olhando para cimaMethodInfo
. Eu suponho que isso não é possível, porque o tipo não é conhecido em tempo de compilação.
Se isso não for possível, uma solução eficiente que jogaria a exceção mais 'nativamente' seria a próxima melhor opção.
Editar: Exemplos de código atualizados para esclarecer que estou usando o IoC (Ninject) para criar oICommandHandler
em tempo de execução, nãoActivator.CreateInstance()
como eu coloquei primeiro. Incluído um exemplo de como isso seria usado conforme solicitado:
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'
Editar 2: Como sugerido abaixo, não posso lançar o resultado deIoC.Get(handlerType)
paraICommandHandler<T>
porque eu tenho umInvalidCastException
em tempo de execução. Isso ocorre porque em tempo de execuçãoT
é na verdadeICommand
, Eu assumo porque as classes de comando estão chegando ao WCF e de alguma forma conseguem perder sua forte digitação. O código que chama o despachante é parecido com:
[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);
}
}