I have made a little CQRS API for our system
I tried to replace some reflection code by using the dynamic keyword but it does not work
Each command handler is Generic CommandHandler<TCommand> with a Method void Execute(TCommand Command)
With reflection it works
public void Invoke(Contracts.Commands.Command command)
{
var handlerType = types[command.GetType()];
var handler = kernel.Get(handlerType);
var method = handlerType.GetMethod("Execute");
method.Invoke(handler, new object[] { command });
}
Kernel.Get is the untyped version of kernel.Get<T> in our IoC (Ninject). This works and the generic method Execute of T fires
This code fails with a argument mismatch exception
public void Invoke(Contracts.Commands.Command command)
{
var handlerType = types[command.GetType()];
dynamic handler = kernel.Get(handlerType);
handler.Execute(command);
}
If i statically declare the type it works with dynamic
dynamic handler = new TestCommandHandler();
handler.Execute(new TestCommand());
edit: Some more info to answer questions in comments
- handlerType is a concrete class that implements the abstract class
CommandHandler<TCommand> where TCommand : Command - The execute method is declared in the abstract class as
public virtual void Execute(TCommand command) - TestCommand implements the abstract Class Command
-
Strange thing is that the same Handler and command works if I statically declare them strongly typed, last example (In the real world example I have dependencies in the constructor though
edit2
-
Stacktrace
at CallSite.Target(Closure , CallSite , Object , Command ) at
System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite
site, T0 arg0, T1 arg1) at
XXX.Business.Command.CommandHandlerInvoker.Invoke(Command
command) in
C:\XXX.Business\Command\CommandHandlerInvoker.cs:line
29 at XXX.Web.XXXService.Execute(Command command) in
C:\XXX.Web\ExfiService.svc.cs:line 29 at
XXX.Web.Controllers.ComplianceController.XXX(XXXViewModel
viewModel) in
C:\XXX.Web\Controllers\XXXController.cs:line
52 at lambda_method(Closure , ControllerBase , Object[] ) at
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase
controller, Object[] parameters) at
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext
controllerContext, IDictionary2 parameters) at2
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext
controllerContext, ActionDescriptor actionDescriptor, IDictionary
parameters) at
System.Web.Mvc.ControllerActionInvoker.<>c_DisplayClass15.b_12()
at
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter
filter, ActionExecutingContext preContext, Func`1 continuation)
- Exception
The best overloaded method match for
XXX.Business.Command.CommandHandler<XXX.Contracts.Commands.TestCommand>.Execute(XXX.Contracts.Commands.TestCommand)' has some invalid arguments
Error message is stating the abstract class of T not the concrete class, but handlerType in above code is the concrete class
You need to change your code to look like this:
Notice that the
commandparameter is first assigned to a dynamic local variable (cmd). This allows the type ofcmdto be evaluated at runtime and keeps the call toExecutetruly dynamic; where as NOT doing it this way, theExecutemethod is assumed to have a fixed signature ofExecute(Contracts.Commands.Command command)