public void Create_ReturnsBinding_IfContainsResolvedGenericParameter() { // Arrange string parameterName = "Parameter"; Type parameterType = typeof(IEnumerable <int>); // Act IBinding binding = InvokeBinding.Create(parameterName, parameterType); // Assert Assert.NotNull(binding); }
public void Create_ReturnsNull_IfByRefParameter() { // Arrange string parameterName = "Parameter"; Type parameterType = typeof(int).MakeByRefType(); // Act IBinding binding = InvokeBinding.Create(parameterName, parameterType); // Assert Assert.Null(binding); }
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; 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; if (triggerBinding != null) { bindingDataContract = triggerBinding.BindingDataContract; } else { bindingDataContract = null; } bool hasParameterBindingAttribute = false; Exception invalidInvokeBindingException = null; foreach (ParameterInfo parameter in parameters) { if (parameter == triggerParameter) { continue; } IBinding binding = await _bindingProvider.TryCreateAsync(new BindingProviderContext(parameter, bindingDataContract, cancellationToken)); if (binding == null) { if (triggerBinding != null && !hasNoAutomaticTriggerAttribute) { throw new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } 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(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.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)) { this._trace.Warning($"Function '{method.Name}' is async but does not return a Task. Your function may not run correctly."); } Type returnType = method.ReturnType; if (returnType != typeof(void) && returnType != typeof(Task)) { throw new InvalidOperationException("Functions must return Task or void."); } 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); IFunctionInvoker invoker = FunctionInvokerFactory.Create(method, _activator); IFunctionDefinition functionDefinition; if (triggerBinding != null) { functionDefinition = CreateTriggeredFunctionDefinition(triggerBinding, triggerParameterName, _executor, functionDescriptor, nonTriggerBindings, invoker, _singletonManager); if (hasNoAutomaticTriggerAttribute && functionDefinition != null) { functionDefinition = new FunctionDefinition(functionDescriptor, functionDefinition.InstanceFactory, listenerFactory: null); } } else { IFunctionInstanceFactory instanceFactory = new FunctionInstanceFactory(new FunctionBinding(functionDescriptor, nonTriggerBindings, _singletonManager), invoker, functionDescriptor); functionDefinition = new FunctionDefinition(functionDescriptor, instanceFactory, listenerFactory: null); } index.Add(functionDefinition, functionDescriptor, method); }
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; if (triggerBinding != null) { 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)) { 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) { throw new InvalidOperationException( string.Format(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.ExtensionInitializationMessage)); } 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(Constants.UnableToBindParameterFormat, parameter.Name, parameter.ParameterType.Name, Constants.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 '{method.Name}' 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); 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); functionDefinition = new FunctionDefinition(functionDescriptor, instanceFactory, listenerFactory: null); } index.Add(functionDefinition, functionDescriptor, method); }