private MethodInfo GetAvailabilityMethod(string?availabilityMethodName)
    {
        if (availabilityMethodName == null)
        {
            throw new Exception("If the action is conditionally available you need to provide a method to calculate availability.");
        }

        var declaringType = ActionMethod.DeclaringType;
        var method        = declaringType.GetMethod(availabilityMethodName);

        if (method == null)
        {
            throw new Exception($"Availability Method {availabilityMethodName} was not found on type {declaringType.FullName}");
        }

        if (method.ReturnType != typeof(bool))
        {
            throw new Exception($"AvailabilityCheck method ({availabilityMethodName}) MUST return bool.");
        }

        var actionBindingParameterType = ActionMethod.GetParameters().First().ParameterType;
        var methodParameters           = method.GetParameters();

        if (methodParameters.Count() != 1 || methodParameters.First().ParameterType != actionBindingParameterType)
        {
            throw new Exception($"AvailabilityCheck method was expected to have this signature 'bool {availabilityMethodName}({actionBindingParameterType.FullName})'");
        }

        return(method);
    }
        private static object[] CreateActionArguments(CliActionContext context)
        {
            var parameters = context.ActionRoute !.ActionMethod.GetParameters();

            switch (parameters.Length)
            {
            case 0:
                return(Array.Empty <object>());

            case > 1:
                throw new TargetParameterCountException("Action argument count must be 0 or 1");

            default:
            {
                var option = new OptionParser(parameters.First().ParameterType).Parse(context.Arguments.ToArray());

                return(new[] { option });
            }
            }
        }
        public override IAsyncResult BeginExecute(ControllerContext controllerContext, IDictionary <string, object> parameters, AsyncCallback callback, object state)
        {
            if (controllerContext == null)
            {
                throw new ArgumentNullException("controllerContext");
            }
            if (parameters == null)
            {
                throw new ArgumentNullException("parameters");
            }

            AsyncManager         asyncHelper         = GetAsyncManager(controllerContext.Controller);
            SingleFireEvent      setupCompletedEvent = new SingleFireEvent();
            ContinuationListener listener            = new ContinuationListener();
            object theDelegate = null;

            BeginInvokeCallback beginCallback = (innerCallback, innerState) => {
                ManualAsyncResult asyncResult = new ManualAsyncResult()
                {
                    AsyncState = innerState
                };

                // Get parameters for async setup method, then execute
                ParameterInfo[] setupParametersInfos    = ActionMethod.GetParameters();
                var             rawSetupParameterValues = from parameterInfo in setupParametersInfos
                                                          select ExtractParameterFromDictionary(parameterInfo, parameters, ActionMethod);

                object[] setupParametersArray = rawSetupParameterValues.ToArray();

                // to simplify the logic, force an asynchronous callback
                asyncHelper.OutstandingOperations.Completed += delegate {
                    if (setupCompletedEvent.Signal())
                    {
                        listener.SetContinuation(() => {
                            ThreadPool.QueueUserWorkItem(o => {
                                asyncResult.MarkCompleted(false /* completedSynchronously */, innerCallback);
                            });
                        });
                    }
                };

                MethodDispatcher setupDispatcher = DispatcherCache.GetDispatcher(ActionMethod);
                asyncHelper.OutstandingOperations.Increment();
                object returnedDelegate = setupDispatcher.Execute(controllerContext.Controller, setupParametersArray);
                ValidateDelegateNotNull(returnedDelegate, ActionMethod);
                asyncHelper.OutstandingOperations.Decrement();

                Thread.VolatileWrite(ref theDelegate, returnedDelegate);
                listener.Signal();
                return(asyncResult);
            };

            AsyncCallback <object> endCallback = ar => {
                if (setupCompletedEvent.Signal())
                {
                    // the setup method did not complete before this callback executed
                    throw new InvalidOperationException(MvcResources.AsyncActionDescriptor_EndExecuteCalledPrematurely);
                }

                object     returnedDelegate = Thread.VolatileRead(ref theDelegate);
                MethodInfo invokeMethod     = returnedDelegate.GetType().GetMethod("Invoke", Type.EmptyTypes);

                MethodDispatcher invokeDispatcher  = DispatcherCache.GetDispatcher(invokeMethod);
                object           actionReturnValue = invokeDispatcher.Execute(returnedDelegate, _emptyParameters);
                return(actionReturnValue);
            };

            // Set the timeout and go
            int timeout = asyncHelper.Timeout;

            return(AsyncResultWrapper.WrapWithTimeout(callback, state, beginCallback, endCallback, timeout, _executeTag));
        }