public async Task <ICommandResult> Execute(Command command)
        {
            if (command == null)
            {
                throw new ArgumentNullException(nameof(command));
            }

            var iterator = new CommandIterator(command);

            var currentController = (CommandController <TContext>)Activator.CreateInstance(typeof(TMainController), _context, iterator);

            while (true)
            {
                ICommandResult result;
                if (iterator.HasNext)
                {
                    result = await ExecuteControllerMethod(currentController, iterator, iterator.GetNext()).ConfigureAwait(false);
                }
                else
                {
                    result = await ExecuteControllerMethod(currentController, iterator).ConfigureAwait(false);
                }

                if (result is ForwardResult forwardResult)
                {
                    var args = new List <object>();
                    args.Add(_context);
                    args.Add(iterator);
                    args.AddRange(forwardResult.ControllerArgs);

                    currentController = (CommandController <TContext>)Activator.CreateInstance(forwardResult.ControllerType, args.ToArray());
                }
                else
                {
                    return(result);
                }
            }

            throw new IncompleteCommandException("Command iterator reached end but execution chain goes on.");
        }
        private Task <ICommandResult> ExecuteControllerMethod(CommandController <TContext> controller, CommandIterator commandIterator, string methodName = default)
        {
            MethodInfo method;

            if (methodName == default)
            {
                method = controller.GetType().GetMethods().FirstOrDefault(m => m.GetCustomAttribute <DefaultCommandAttribute>(true) != null);
                if (method == null)
                {
                    throw new InvalidCommandException($"Controller {controller} has no default command handler.");
                }
            }
            else
            {
                method = controller.GetType().GetMethods().FirstOrDefault(m => m.GetCustomAttribute <CommandAttribute>(true)?.Command == methodName);
                if (method == null)
                {
                    throw new InvalidCommandException($"Controller {controller} has no handler for the {methodName} command.");
                }
            }

            var args = new List <object>();

            foreach (ParameterInfo parameter in method.GetParameters())
            {
                if (!commandIterator.HasNext)
                {
                    throw new IncompleteCommandException("Command iterator reached end but command method requires argument.");
                }

                args.Add(commandIterator.GetNext(parameter.ParameterType));
            }

            return((Task <ICommandResult>)method.Invoke(controller, args.ToArray()));
        }
 protected CommandController(TContext context, CommandIterator commandIterator)
 {
     Context         = context ?? throw new ArgumentNullException(nameof(context));
     CommandIterator = commandIterator ?? throw new ArgumentNullException(nameof(commandIterator));
 }