public async Task Invoke_UsesDefaultValuesIfNotBound()
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
                BoundProperties = new List<ParameterDescriptor>(),
                MethodInfo = typeof(TestController).GetTypeInfo()
                    .DeclaredMethods
                    .First(m => m.Name.Equals("ActionMethodWithDefaultValues", StringComparison.Ordinal)),

                Parameters = new List<ParameterDescriptor>
                {
                    new ParameterDescriptor
                    {
                        Name = "value",
                        ParameterType = typeof(int),
                        BindingInfo = new BindingInfo(),
                    }
                },
                FilterDescriptors = new List<FilterDescriptor>()
            };

            var context = new Mock<HttpContext>();
            context.SetupGet(c => c.Items)
                   .Returns(new Dictionary<object, object>());
            context.Setup(c => c.RequestServices.GetService(typeof(ILoggerFactory)))
                       .Returns(new NullLoggerFactory());

            var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor);

            var controllerFactory = new Mock<IControllerFactory>();
            controllerFactory.Setup(c => c.CreateController(It.IsAny<ControllerContext>()))
                             .Returns(new TestController());

            var metadataProvider = new EmptyModelMetadataProvider();

            var invoker = new ControllerActionInvoker(
                actionContext,
                CreateFilterCache(),
                controllerFactory.Object,
                actionDescriptor,
                new IInputFormatter[0],
                new ControllerArgumentBinder(
                    metadataProvider,
                    TestModelBinderFactory.CreateDefault(metadataProvider),
                    new DefaultObjectValidator(metadataProvider, new ValidatorCache())),
                new IModelValidatorProvider[0],
                new IValueProviderFactory[0],
                new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
                new DiagnosticListener("Microsoft.AspNetCore"),
                200);

            // Act
            await invoker.InvokeAsync();

            // Assert
            Assert.Equal(5, context.Object.Items["Result"]);
        }
        public void CreateController_IgnoresPropertiesThatAreNotDecoratedWithAttribute()
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(ControllerWithoutAttributes).GetTypeInfo()
            };

            var context = new ControllerContext()
            {
                ActionDescriptor = actionDescriptor,
                HttpContext = new DefaultHttpContext()
                {
                    RequestServices = GetServices(),
                },
            };
            var factory = CreateControllerFactory(new DefaultControllerActivator(new TypeActivatorCache()));

            // Act
            var result = factory.CreateController(context);

            // Assert
            var controller = Assert.IsType<ControllerWithoutAttributes>(result);
            Assert.Null(controller.ActionContext);
        }
Beispiel #3
0
        public void ExecutingAction_ForControllerAction_WithGivenRouteValues_LogsActionAndRouteData(string expectedRouteValuesLogMessage, params KeyValuePair <string, string>[] routeValues)
        {
            // Arrange
            var testSink      = new TestSink();
            var loggerFactory = new TestLoggerFactory(testSink, enabled: true);
            var logger        = loggerFactory.CreateLogger("test");

            var action = new Controllers.ControllerActionDescriptor
            {
                // Using a generic type to verify the use of a clean name
                ControllerTypeInfo = typeof(ValueTuple <int, string>).GetTypeInfo(),
                MethodInfo         = typeof(object).GetMethod(nameof(ToString)),
            };

            foreach (var routeValue in routeValues)
            {
                action.RouteValues.Add(routeValue);
            }

            // Act
            logger.ExecutingAction(action);

            // Assert
            var write = Assert.Single(testSink.Writes);

            Assert.Equal(
                $"Route matched with {expectedRouteValuesLogMessage}. " +
                "Executing controller action with signature System.String ToString() on controller System.ValueTuple<int, string> (System.Private.CoreLib).",
                write.State.ToString());
        }
        public ControllerActionInvoker(
            ActionContext actionContext,
            ControllerActionInvokerCache controllerActionInvokerCache,
            IControllerFactory controllerFactory,
            ControllerActionDescriptor descriptor,
            IReadOnlyList<IInputFormatter> inputFormatters,
            IControllerActionArgumentBinder argumentBinder,
            IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
            IReadOnlyList<IValueProviderFactory> valueProviderFactories,
            ILogger logger,
            DiagnosticSource diagnosticSource,
            int maxModelValidationErrors)
            : base(
                  actionContext,
                  controllerActionInvokerCache,
                  inputFormatters,
                  modelValidatorProviders,
                  valueProviderFactories,
                  logger,
                  diagnosticSource,
                  maxModelValidationErrors)
        {
            if (controllerFactory == null)
            {
                throw new ArgumentNullException(nameof(controllerFactory));
            }

            if (descriptor == null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            if (argumentBinder == null)
            {
                throw new ArgumentNullException(nameof(argumentBinder));
            }

            _controllerFactory = controllerFactory;
            _descriptor = descriptor;
            _argumentBinder = argumentBinder;

            if (descriptor.MethodInfo == null)
            {
                throw new ArgumentException(
                    Resources.FormatPropertyOfTypeCannotBeNull(
                        nameof(descriptor.MethodInfo),
                        typeof(ControllerActionDescriptor)),
                    nameof(descriptor));
            }
        }
 public ModelBindingActionInvoker(
     ActionContext actionContext,
     ControllerActionInvokerCache controllerActionInvokerCache,
     IControllerFactory controllerFactory,
     ControllerActionDescriptor descriptor,
     IReadOnlyList<IInputFormatter> inputFormatters,
     IControllerActionArgumentBinder controllerActionArgumentBinder,
     IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
     IReadOnlyList<IValueProviderFactory> valueProviderFactories,
     ILogger logger,
     DiagnosticSource diagnosticSource,
     int maxModelValidationErrors)
         : base(actionContext, controllerActionInvokerCache, controllerFactory, descriptor, inputFormatters, controllerActionArgumentBinder, modelValidatorProviders, valueProviderFactories, logger, diagnosticSource, maxModelValidationErrors)
 {
     this.controllerActionDescriptor = descriptor;
 }
 public IActionInvoker CreateModelBindingActionInvoker(
     ActionContext actionContext,
     ControllerActionDescriptor controllerActionDescriptor)
 {
     return new ModelBindingActionInvoker(
         actionContext,
         this.controllerActionInvokerCache,
         this.controllerFactory,
         controllerActionDescriptor,
         this.inputFormatters,
         this.argumentBinder,
         this.modelValidatorProviders,
         this.valueProviderFactories,
         this.logger,
         this.diagnosticSource,
         this.maxModelValidationErrors);
 }
Beispiel #7
0
        public Func <ControllerContext, object> CreateControllerFactory(ControllerActionDescriptor descriptor)
        {
            if (descriptor == null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            var controllerType = descriptor.ControllerTypeInfo?.AsType();

            if (controllerType == null)
            {
                throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                                                nameof(descriptor.ControllerTypeInfo),
                                                nameof(descriptor)),
                                            nameof(descriptor));
            }

            if (_factoryCreateController != null)
            {
                return(_factoryCreateController);
            }

            var controllerActivator = _activatorProvider.CreateActivator(descriptor);
            var propertyActivators  = GetPropertiesToActivate(descriptor);

            object CreateController(ControllerContext controllerContext)
            {
                var controller = controllerActivator(controllerContext);

                for (var i = 0; i < propertyActivators.Length; i++)
                {
                    var propertyActivator = propertyActivators[i];
                    propertyActivator(controllerContext, controller);
                }

                return(controller);
            }

            return(CreateController);
        }
        public void CreateReleaser_InvokesIControllerActivator_IfItIsNotDefaultControllerActivator()
        {
            // Arrange
            var expected  = new object();
            var activator = new Mock <IControllerActivator>();

            activator.Setup(a => a.Release(It.IsAny <ControllerContext>(), expected))
            .Verifiable();
            var activatorProvider = new ControllerActivatorProvider(activator.Object);
            var descriptor        = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(object).GetTypeInfo(),
            };

            // Act
            var releaseDelegate = activatorProvider.CreateReleaser(descriptor);

            releaseDelegate(new ControllerContext(), expected);

            // Assert
            activator.Verify();
        }
        /// <inheritdoc/>
        public Func <ControllerContext, object, ValueTask>?CreateAsyncReleaser(ControllerActionDescriptor descriptor)
        {
            if (descriptor == null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            if (_controllerActivatorReleaseAsync != null)
            {
                return(_controllerActivatorReleaseAsync);
            }

            if (typeof(IAsyncDisposable).GetTypeInfo().IsAssignableFrom(descriptor.ControllerTypeInfo))
            {
                return(_disposeAsync);
            }

            if (typeof(IDisposable).GetTypeInfo().IsAssignableFrom(descriptor.ControllerTypeInfo))
            {
                return(_syncDisposeAsync);
            }

            return(null);
        }
Beispiel #10
0
        public Action <ControllerContext, object> CreateControllerReleaser(ControllerActionDescriptor descriptor)
        {
            if (descriptor == null)
            {
                throw new ArgumentNullException(nameof(descriptor));
            }

            var controllerType = descriptor.ControllerTypeInfo?.AsType();

            if (controllerType == null)
            {
                throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull(
                                                nameof(descriptor.ControllerTypeInfo),
                                                nameof(descriptor)),
                                            nameof(descriptor));
            }

            if (_factoryReleaseController != null)
            {
                return(_factoryReleaseController);
            }

            return(_activatorProvider.CreateReleaser(descriptor));
        }
        public void CreateControllerFactory_UsesControllerActivatorAndPropertyActivator()
        {
            // Arrange
            var expectedProperty1  = new object();
            var expectedProperty2  = new object();
            var expectedController = new TestController();
            var factory            = new DefaultControllerFactory(
                Mock.Of <IControllerActivator>(),
                Enumerable.Empty <IControllerPropertyActivator>());
            var activatorProvider = new Mock <IControllerActivatorProvider>();

            activatorProvider.Setup(p => p.CreateActivator(It.IsAny <ControllerActionDescriptor>()))
            .Returns(_ => expectedController)
            .Verifiable();

            var propertyActivator1 = new Mock <IControllerPropertyActivator>();

            propertyActivator1.Setup(p => p.GetActivatorDelegate(It.IsAny <ControllerActionDescriptor>()))
            .Returns((context, controllerObject) =>
            {
                ((TestController)controllerObject).ActivatedValue1 = expectedProperty1;
            })
            .Verifiable();

            var propertyActivator2 = new Mock <IControllerPropertyActivator>();

            propertyActivator2.Setup(p => p.GetActivatorDelegate(It.IsAny <ControllerActionDescriptor>()))
            .Returns((context, controllerObject) =>
            {
                ((TestController)controllerObject).ActivatedValue2 = expectedProperty2;
            })
            .Verifiable();

            var propertyActivators = new[]
            {
                propertyActivator1.Object,
                propertyActivator2.Object,
            };
            var provider = new ControllerFactoryProvider(
                activatorProvider.Object,
                factory,
                propertyActivators);
            var descriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
            };

            // Act
            var factoryDelegate = provider.CreateControllerFactory(descriptor);
            var controller      = factoryDelegate(new ControllerContext());

            // Assert
            var actual = Assert.IsType <TestController>(controller);

            Assert.Same(expectedController, actual);
            Assert.Same(expectedProperty1, actual.ActivatedValue1);
            Assert.Same(expectedProperty2, actual.ActivatedValue2);
            activatorProvider.Verify();
            propertyActivator1.Verify();
            propertyActivator2.Verify();
        }
        public void WithCustomControllerContextShouldSetItToAccessor()
        {
            MyMvc
                .IsUsingDefaultConfiguration()
                .WithServices(services =>
                {
                    services.AddActionContextAccessor();
                });

            var actionDescriptor = new ControllerActionDescriptor { Name = "Test" };
            var actionContext = new ControllerContext { ActionDescriptor = actionDescriptor };

            MyMvc
                .Controller<ActionContextController>()
                .WithControllerContext(actionContext)
                .ShouldPassFor()
                .TheController(controller =>
                {
                    Assert.NotNull(controller);
                    Assert.NotNull(controller.Context);
                    Assert.Equal("Test", controller.Context.ActionDescriptor.Name);
                });

            MyMvc.IsUsingDefaultConfiguration();
        }
 public TestControllerActionInvoker(
     ActionContext actionContext,
     IFilterProvider[] filterProviders,
     MockControllerFactory controllerFactory,
     ControllerActionDescriptor descriptor,
     IReadOnlyList<IInputFormatter> inputFormatters,
     IControllerActionArgumentBinder controllerActionArgumentBinder,
     IReadOnlyList<IModelValidatorProvider> modelValidatorProviders,
     IReadOnlyList<IValueProviderFactory> valueProviderFactories,
     ILogger logger,
     DiagnosticSource diagnosticSource,
     int maxAllowedErrorsInModelState)
     : base(
           actionContext,
           CreateFilterCache(filterProviders),
           controllerFactory,
           descriptor,
           inputFormatters,
           controllerActionArgumentBinder,
           modelValidatorProviders,
           valueProviderFactories,
           logger,
           diagnosticSource,
           maxAllowedErrorsInModelState)
 {
     ControllerFactory = controllerFactory;
 }
        public async Task ValidationIsTriggered_OnFromBodyModels(List<ParameterDescriptor> parameters)
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor()
            {
                BoundProperties = new List<ParameterDescriptor>(),
                Parameters = parameters
            };
            var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();

            var testContext = ModelBindingTestHelper.GetTestContext(
                request =>
                {
                    request.QueryString = new QueryString("?accountId=30");
                    request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{\"accountId\": 15,\"amount\": 250.0}"));
                    request.ContentType = "application/json";
                },
                actionDescriptor: actionDescriptor);

            var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
            var modelState = testContext.ModelState;

            // Act
            await argumentBinder.BindArgumentsAsync(testContext, new TestController(), arguments);

            // Assert
            Assert.False(modelState.IsValid);

            var entry = Assert.Single(
                modelState,
                e => string.Equals(e.Key, "AccountId", StringComparison.OrdinalIgnoreCase)).Value;
            var error = Assert.Single(entry.Errors);
            Assert.Equal("The field AccountId must be between 25 and 50.", error.ErrorMessage);
        }
        public async Task MultipleActionParameter_ValidModelState(List<ParameterDescriptor> parameters)
        {
            // Since validation attribute is only present on the FromBody model's property(TransferInfo's AccountId),
            // validation should not trigger for the parameter which is bound from Uri.

            // Arrange
            var actionDescriptor = new ControllerActionDescriptor()
            {
                BoundProperties = new List<ParameterDescriptor>(),
                Parameters = parameters
            };
            var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();

            var testContext = ModelBindingTestHelper.GetTestContext(
                request =>
                {
                    request.QueryString = new QueryString("?accountId=10");
                    request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{\"accountId\": 40,\"amount\": 250.0}"));
                    request.ContentType = "application/json";
                },
                actionDescriptor: actionDescriptor);

            var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
            var modelState = testContext.ModelState;

            // Act
            await argumentBinder.BindArgumentsAsync(testContext, new TestController(), arguments);

            // Assert
            Assert.True(modelState.IsValid);
            object value;
            Assert.True(arguments.TryGetValue("accountId", out value));
            var accountId = Assert.IsType<int>(value);
            Assert.Equal(10, accountId);
            Assert.True(arguments.TryGetValue("transferInfo", out value));
            var transferInfo = Assert.IsType<TransferInfo>(value);
            Assert.NotNull(transferInfo);
            Assert.Equal(40, transferInfo.AccountId);
            Assert.Equal(250.0, transferInfo.Amount);
        }
        public void CreateController_UsesControllerActivatorToInstantiateController()
        {
            // Arrange
            var expected = new MyController();
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(MyController).GetTypeInfo()
            };

            var context = new ControllerContext()
            {
                ActionDescriptor = actionDescriptor,
                HttpContext = new DefaultHttpContext()
                {
                    RequestServices = GetServices(),
                },
            };

            var activator = new Mock<IControllerActivator>();
            activator.Setup(a => a.Create(context))
                     .Returns(expected)
                     .Verifiable();

            var controllerFactory = CreateControllerFactory(activator.Object);

            // Act
            var result = controllerFactory.CreateController(context);

            // Assert
            var controller = Assert.IsType<MyController>(result);
            Assert.Same(expected, controller);
            activator.Verify();
        }
 public static string?AreaName(this ControllerActionDescriptor actionDescriptor) => actionDescriptor.ControllerTypeInfo.GetCustomAttribute <AreaAttribute>()?.RouteValue;
        public static (string?areaName, string controllerName, string actionName, string path) GetAreaControllerAction(this ControllerActionDescriptor actionDescriptor)
        {
            var area       = actionDescriptor.AreaName();
            var controller = actionDescriptor.ControllerName;
            var action     = actionDescriptor.ActionName;
            var separator  = Path.AltDirectorySeparatorChar;

            return(area, controller, action, $"{separator}{area}{separator}{controller}{separator}{action}");
        }
        public void CreateController_SetsControllerContext()
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(ControllerWithAttributes).GetTypeInfo()
            };

            var context = new ControllerContext()
            {
                ActionDescriptor = actionDescriptor,
                HttpContext = new DefaultHttpContext()
                {
                    RequestServices = GetServices(),
                },
            };
            var factory = CreateControllerFactory(new DefaultControllerActivator(new TypeActivatorCache()));

            // Act
            var result = factory.CreateController(context);

            // Assert
            var controller = Assert.IsType<ControllerWithAttributes>(result);
            Assert.Same(context, controller.ControllerContext);
        }
        private static IDictionary<string, object> GetRouteValues(
            MethodInfo methodInfo,
            MethodCallExpression methodCallExpression,
            ControllerActionDescriptor controllerActionDescriptor,
            bool considerParameterDescriptors)
        {
            var result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

            var arguments = methodCallExpression.Arguments;
            if (arguments.Count == 0)
            {
                return result;
            }

            var methodParameters = methodInfo.GetParameters();

            var parameterDescriptors = new Dictionary<string, string>();
            if (considerParameterDescriptors)
            {
                var parameters = controllerActionDescriptor.Parameters;
                for (int i = 0; i < parameters.Count; i++)
                {
                    var parameter = parameters[i];
                    if (parameter.BindingInfo != null)
                    {
                        parameterDescriptors.Add(parameter.Name, parameter.BindingInfo.BinderModelName);
                    }
                }
            }

            for (var i = 0; i < arguments.Count; i++)
            {
                var methodParameterName = methodParameters[i].Name;
                if (considerParameterDescriptors && parameterDescriptors.ContainsKey(methodParameterName))
                {
                    methodParameterName = parameterDescriptors[methodParameterName] ?? methodParameterName;
                }

                var value = ExpressionParser.ResolveExpressionValue(arguments[i]);
                if (value == null)
                {
                    continue;
                }

                result[methodParameterName] = value;
            }

            return result;
        }
        public void WithControllerContextFuncShouldSetItToAccessor()
        {
            MyApplication
                .StartsFrom<DefaultStartup>()
                .WithServices(services =>
                {
                    services.AddActionContextAccessor();
                });

            var actionDescriptor = new ControllerActionDescriptor { DisplayName = "Test" };;

            MyController<ActionContextController>
                .Instance()
                .WithControllerContext(controllerContext =>
                {
                    controllerContext.ActionDescriptor = actionDescriptor;
                })
                .ShouldPassForThe<ActionContextController>(controller =>
                {
                    Assert.NotNull(controller);
                    Assert.NotNull(controller.Context);
                    Assert.Equal("Test", controller.Context.ActionDescriptor.DisplayName);
                });

            MyApplication.StartsFrom<DefaultStartup>();
        }
 public IActionInvoker CreateModelBindingActionInvoker(ActionContext actionContext, ControllerActionDescriptor controllerActionDescriptor)
 {
     return null;
 }
Beispiel #23
0
        public static ControllerBinderDelegate CreateBinderDelegate(
            ParameterBinder parameterBinder,
            IModelBinderFactory modelBinderFactory,
            IModelMetadataProvider modelMetadataProvider,
            ControllerActionDescriptor actionDescriptor,
            MvcOptions mvcOptions)
        {
            if (parameterBinder == null)
            {
                throw new ArgumentNullException(nameof(parameterBinder));
            }

            if (modelBinderFactory == null)
            {
                throw new ArgumentNullException(nameof(modelBinderFactory));
            }

            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            if (mvcOptions == null)
            {
                throw new ArgumentNullException(nameof(mvcOptions));
            }

            var parameterBindingInfo = GetParameterBindingInfo(
                modelBinderFactory,
                modelMetadataProvider,
                actionDescriptor,
                mvcOptions);
            var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);

            if (parameterBindingInfo == null && propertyBindingInfo == null)
            {
                return(null);
            }

            return(Bind);

            async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object> arguments)
            {
                var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);

                var parameters = actionDescriptor.Parameters;

                for (var i = 0; i < parameters.Count; i++)
                {
                    var parameter     = parameters[i];
                    var bindingInfo   = parameterBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        arguments[parameter.Name] = result.Model;
                    }
                }

                var properties = actionDescriptor.BoundProperties;

                for (var i = 0; i < properties.Count; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        property,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model);
                    }
                }
            }
        }
        public void CreateController_ThrowsIConstructorCannotBeActivated()
        {
            // Arrange
            var actionDescriptor = new ControllerActionDescriptor
            {
                ControllerTypeInfo = typeof(ControllerThatCannotBeActivated).GetTypeInfo()
            };

            var context = new ControllerContext()
            {
                ActionDescriptor = actionDescriptor,
                HttpContext = new DefaultHttpContext()
                {
                    RequestServices = GetServices(),
                },
            };
            var factory = CreateControllerFactory(new DefaultControllerActivator(new TypeActivatorCache()));

            // Act and Assert
            var exception = Assert.Throws<InvalidOperationException>(() => factory.CreateController(context));
            Assert.Equal(
                $"Unable to resolve service for type '{typeof(TestService).FullName}' while attempting to activate " +
                $"'{typeof(ControllerThatCannotBeActivated).FullName}'.",
                exception.Message);
        }
        private static ActionDescriptor CreateAction(string area, string controller, string action)
        {
            var actionDescriptor = new ControllerActionDescriptor()
            {
                ActionName = string.Format("Area: {0}, Controller: {1}, Action: {2}", area, controller, action),
                Parameters = new List<ParameterDescriptor>(),
            };

            actionDescriptor.RouteValues.Add("area", area);
            actionDescriptor.RouteValues.Add("controller", controller);
            actionDescriptor.RouteValues.Add("action", action);

            return actionDescriptor;
        }
        //public static (string areaName, string controllerName, string actionName, string path) MvcRoutePath(this ControllerActionDescriptor actionDescriptor) {

        #region "DebugObject"

        public static ControllerActionDescriptorDebugObject GetDebugObject(this ControllerActionDescriptor controllerActionDescriptor) => new ControllerActionDescriptorDebugObject(controllerActionDescriptor);
        public static ControllerBinderDelegate?CreateBinderDelegate(
            ParameterBinder parameterBinder,
            IModelBinderFactory modelBinderFactory,
            IModelMetadataProvider modelMetadataProvider,
            ControllerActionDescriptor actionDescriptor,
            MvcOptions mvcOptions)
        {
            if (parameterBinder == null)
            {
                throw new ArgumentNullException(nameof(parameterBinder));
            }

            if (modelBinderFactory == null)
            {
                throw new ArgumentNullException(nameof(modelBinderFactory));
            }

            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            if (mvcOptions == null)
            {
                throw new ArgumentNullException(nameof(mvcOptions));
            }

            var parameterBindingInfo = GetParameterBindingInfo(
                modelBinderFactory,
                modelMetadataProvider,
                actionDescriptor);
            var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);

            if (parameterBindingInfo == null && propertyBindingInfo == null)
            {
                return(null);
            }

            var parameters = actionDescriptor.Parameters switch
            {
                List <ParameterDescriptor> list => list.ToArray(),
                _ => actionDescriptor.Parameters.ToArray()
            };

            var properties = actionDescriptor.BoundProperties switch
            {
                List <ParameterDescriptor> list => list.ToArray(),
                _ => actionDescriptor.BoundProperties.ToArray()
            };

            return(Bind);

            async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object?> arguments)
            {
                var(success, valueProvider) = await CompositeValueProvider.TryCreateAsync(controllerContext, controllerContext.ValueProviderFactories);

                if (!success)
                {
                    return;
                }

                Debug.Assert(valueProvider is not null);

                for (var i = 0; i < parameters.Length; i++)
                {
                    var parameter     = parameters[i];
                    var bindingInfo   = parameterBindingInfo ![i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null,
                        container : null); // Parameters do not have containers.

                    if (result.IsModelSet)
                    {
                        arguments[parameter.Name] = result.Model;
                    }
                }

                for (var i = 0; i < properties.Length; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo ![i];
        private TestControllerActionInvoker CreateInvoker(
            IFilterMetadata[] filters,
            bool actionThrows = false,
            int maxAllowedErrorsInModelState = 200)
        {
            var actionDescriptor = new ControllerActionDescriptor()
            {
                FilterDescriptors = new List<FilterDescriptor>(),
                Parameters = new List<ParameterDescriptor>(),
            };

            if (actionThrows)
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ThrowingActionMethod");
            }
            else
            {
                actionDescriptor.MethodInfo = typeof(ControllerActionInvokerTest).GetMethod("ActionMethod");
            }
            actionDescriptor.ControllerTypeInfo = typeof(ControllerActionInvokerTest).GetTypeInfo();

            var httpContext = new Mock<HttpContext>(MockBehavior.Loose);

            var http = GetHttpContext();

            var httpRequest = http.Request;
            var httpResponse = http.Response;

            httpContext.SetupGet(c => c.Request).Returns(httpRequest);
            httpContext.SetupGet(c => c.Response).Returns(httpResponse);
            httpContext
                .Setup(o => o.RequestServices.GetService(typeof(ILoggerFactory)))
                .Returns(NullLoggerFactory.Instance);

            httpResponse.Body = new MemoryStream();

            var formatter = new Mock<IOutputFormatter>();
            formatter
                .Setup(f => f.CanWriteResult(It.IsAny<OutputFormatterCanWriteContext>()))
                .Returns(true);

            formatter
                .Setup(f => f.WriteAsync(It.IsAny<OutputFormatterWriteContext>()))
                .Returns<OutputFormatterWriteContext>(async c =>
                {
                    await c.HttpContext.Response.WriteAsync(c.Object.ToString());
                });

            var options = new MvcOptions();
            options.OutputFormatters.Add(formatter.Object);

            var optionsAccessor = new Mock<IOptions<MvcOptions>>();
            optionsAccessor
                .SetupGet(o => o.Value)
                .Returns(options);

            httpContext
                .Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
                .Returns(optionsAccessor.Object);

            var actionContext = new ActionContext(
                httpContext: httpContext.Object,
                routeData: new RouteData(),
                actionDescriptor: actionDescriptor);

            var filterProvider = new Mock<IFilterProvider>(MockBehavior.Strict);
            filterProvider
                .Setup(fp => fp.OnProvidersExecuting(It.IsAny<FilterProviderContext>()))
                .Callback<FilterProviderContext>(context =>
                    {
                        foreach (var filterMetadata in filters)
                        {
                            context.Results.Add(new FilterItem(new FilterDescriptor(filterMetadata, FilterScope.Action))
                            {
                                Filter = filterMetadata,
                            });
                        }
                    });

            filterProvider
                .Setup(fp => fp.OnProvidersExecuted(It.IsAny<FilterProviderContext>()))
                .Verifiable();

            var actionArgumentsBinder = new Mock<IControllerActionArgumentBinder>();
            actionArgumentsBinder.Setup(
                    b => b.BindActionArgumentsAsync(It.IsAny<ControllerContext>(), It.IsAny<object>()))
                .Returns(Task.FromResult<IDictionary<string, object>>(new Dictionary<string, object>()));

            filterProvider
                .SetupGet(fp => fp.Order)
                .Returns(-1000);

            var invoker = new TestControllerActionInvoker(
                actionContext,
                new[] { filterProvider.Object },
                new MockControllerFactory(this),
                actionDescriptor,
                new IInputFormatter[0],
                actionArgumentsBinder.Object,
                new IModelValidatorProvider[0],
                new IValueProviderFactory[0],
                new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
                new DiagnosticListener("Microsoft.AspNetCore"),
                maxAllowedErrorsInModelState);
            return invoker;
        }