internal async Task IndexMethodAsyncCore()

in src/Microsoft.Azure.WebJobs.Host/Indexers/FunctionIndexer.cs [161:350]


        internal async Task IndexMethodAsyncCore(MethodInfo method, IFunctionIndexCollector index, CancellationToken cancellationToken)
        {
            Debug.Assert(method != null);
            bool hasNoAutomaticTriggerAttribute = method.GetCustomAttribute<NoAutomaticTriggerAttribute>() != null;

            ITriggerBinding triggerBinding = null;
            ParameterInfo triggerParameter = null;
            IEnumerable<ParameterInfo> parameters = method.GetParameters();

            foreach (ParameterInfo parameter in parameters)
            {
                ITriggerBinding possibleTriggerBinding = await _triggerBindingProvider.TryCreateAsync(new TriggerBindingProviderContext(parameter, cancellationToken));

                if (possibleTriggerBinding != null)
                {
                    if (triggerBinding == null)
                    {
                        triggerBinding = possibleTriggerBinding;
                        triggerParameter = parameter;
                    }
                    else
                    {
                        throw new InvalidOperationException("More than one trigger per function is not allowed.");
                    }
                }
            }

            Dictionary<string, IBinding> nonTriggerBindings = new Dictionary<string, IBinding>();
            IReadOnlyDictionary<string, Type> bindingDataContract;

            string sharedListenerId = null;
            if (triggerBinding != null)
            {
                sharedListenerId = GetSharedListenerIdOrNull(triggerBinding);
                bindingDataContract = triggerBinding.BindingDataContract;

                // See if a regular binding can handle it. 
                IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(triggerParameter, bindingDataContract, cancellationToken));
                if (binding != null)
                {
                    triggerBinding = new TriggerWrapper(triggerBinding, binding);
                }
            }
            else
            {
                bindingDataContract = null;
            }

            bool hasParameterBindingAttribute = false;
            Exception invalidInvokeBindingException = null;

            ReturnParameterInfo returnParameter = null;
            bool triggerHasReturnBinding = false;

            if (TypeUtility.TryGetReturnType(method, out Type methodReturnType) && !IsUnitType(methodReturnType))
            {
                if (bindingDataContract != null && bindingDataContract.TryGetValue(ReturnParamName, out Type triggerReturnType))
                {
                    // The trigger will handle the return value.
                    triggerHasReturnBinding = true;
                }

                // We treat binding to the return type the same as binding to an 'out T' parameter. 
                // An explicit return binding takes precedence over an implicit trigger binding. 
                returnParameter = new ReturnParameterInfo(method, methodReturnType);
                parameters = parameters.Concat(new ParameterInfo[] { returnParameter });
            }

            foreach (ParameterInfo parameter in parameters)
            {
                if (parameter == triggerParameter)
                {
                    continue;
                }

                IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(parameter, bindingDataContract, cancellationToken));
                if (binding == null)
                {
                    if (parameter == returnParameter)
                    {
                        if (triggerHasReturnBinding)
                        {
                            // Ok. Skip and let trigger own the return binding. 
                            continue;
                        }
                        else
                        {
                            // If the method has a return value, then we must have a binding to it. 
                            // This is similar to the error we used to throw. 
                            invalidInvokeBindingException = new InvalidOperationException("Functions must return Task or void, have a binding attribute for the return value, or be triggered by a binding that natively supports return values.");
                        }
                    }

                    if (triggerBinding != null && !hasNoAutomaticTriggerAttribute)
                    {
                        string extendedBindingErrorHelpText = Resource.ExtensionInitializationMessage;

                        if (bindingDataContract != null)
                        {
                            // If a non-trigger/non-attributed parameter binding fails, check to see if we've successfully bound
                            // a trigger parameter and have a binding contract. If so, it might be that the customer is trying to
                            // bind to a contract member. If the parameter type matches one of the binding contract members,
                            // extend the binding error by indicating the valid parameter names for that Type.
                            string[] matchingContractNames = bindingDataContract.Where(p => parameter.ParameterType == p.Value).Select(p => p.Key).ToArray();
                            if (matchingContractNames.Length > 0)
                            {
                                string bindingContractHelpText = $"The binding supports the following parameter names for type {parameter.ParameterType.Name}: ({string.Join(", ", matchingContractNames)}). If you're trying to bind to one of those values, rename your parameter to match the contract name (case insensitive).";
                                extendedBindingErrorHelpText = $"{bindingContractHelpText} {extendedBindingErrorHelpText}";
                            }
                        }

                        string bindingError = string.Format(Resource.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, extendedBindingErrorHelpText);
                        throw new InvalidOperationException(bindingError);
                    }
                    else
                    {
                        // Host.Call-only parameter
                        binding = InvokeBinding.Create(parameter.Name, parameter.ParameterType);
                        if (binding == null && invalidInvokeBindingException == null)
                        {
                            // This function might not have any attribute, in which case we shouldn't throw an
                            // exception when we can't bind it. Instead, save this exception for later once we determine
                            // whether or not it is an SDK function.
                            invalidInvokeBindingException = new InvalidOperationException(
                                string.Format(Resource.UnableToBindParameterFormat,
                                parameter.Name, parameter.ParameterType.Name, Resource.ExtensionInitializationMessage));
                        }
                    }
                }
                else if (!hasParameterBindingAttribute)
                {
                    hasParameterBindingAttribute = binding.FromAttribute;
                }

                nonTriggerBindings.Add(parameter.Name, binding);
            }

            // Only index functions with some kind of attribute on them. Three ways that could happen:
            // 1. There's an attribute on a trigger parameter (all triggers come from attributes).
            // 2. There's an attribute on a non-trigger parameter (some non-trigger bindings come from attributes).
            if (triggerBinding == null && !hasParameterBindingAttribute)
            {
                // 3. There's an attribute on the method itself (NoAutomaticTrigger).
                if (method.GetCustomAttribute<NoAutomaticTriggerAttribute>() == null)
                {
                    return;
                }
            }

            if (TypeUtility.IsAsyncVoid(method))
            {
                string msg = $"Function '{Utility.GetFunctionShortName(method)}' is async but does not return a Task. Your function may not run correctly.";
                _logger?.LogWarning(msg);
            }

            if (invalidInvokeBindingException != null)
            {
                throw invalidInvokeBindingException;
            }

            // Validation: prevent multiple ConsoleOutputs
            if (nonTriggerBindings.OfType<TraceWriterBinding>().Count() > 1)
            {
                throw new InvalidOperationException("Can't have multiple TraceWriter/TextWriter parameters in a single function.");
            }

            string triggerParameterName = triggerParameter != null ? triggerParameter.Name : null;
            FunctionDescriptor functionDescriptor = CreateFunctionDescriptor(method, triggerParameterName, triggerBinding, nonTriggerBindings, sharedListenerId);
            IFunctionInvoker invoker = FunctionInvokerFactory.Create(method, _activator);
            IFunctionDefinition functionDefinition;

            if (triggerBinding != null)
            {
                Type triggerValueType = triggerBinding.TriggerValueType;
                var methodInfo = typeof(FunctionIndexer).GetMethod("CreateTriggeredFunctionDefinition", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(triggerValueType);
                functionDefinition = (FunctionDefinition)methodInfo.Invoke(this, new object[] { triggerBinding, triggerParameterName, functionDescriptor, nonTriggerBindings, invoker });

                if (hasNoAutomaticTriggerAttribute && functionDefinition != null)
                {
                    functionDefinition = new FunctionDefinition(functionDescriptor, functionDefinition.InstanceFactory, listenerFactory: null);
                }
            }
            else
            {
                IFunctionInstanceFactory instanceFactory = new FunctionInstanceFactory(new FunctionBinding(functionDescriptor, nonTriggerBindings, _singletonManager), invoker, functionDescriptor, _instanceServicesFactory);
                functionDefinition = new FunctionDefinition(functionDescriptor, instanceFactory, listenerFactory: null);
            }

            index.Add(functionDefinition, functionDescriptor, method);
        }