Refactor Argument Binder
This change just rearranges some code in the argument binder with a mind towards performance and clarity. We're removing a few Task<T>'s here as well in certain cases, but not yet all of them. We additionally save a dictionary in the case where you have bound properties. Hopefully these changes break the code into more discrete and sensible units without multiple levels of indirection without abstraction. - Main 'driver' code - BindModel - ActivateProperty
This commit is contained in:
parent
4d63ffa879
commit
128d74e2a0
|
|
@ -9,15 +9,20 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
|
|||
/// <summary>
|
||||
/// Provides a dictionary of action arguments.
|
||||
/// </summary>
|
||||
public interface IControllerActionArgumentBinder
|
||||
public interface IControllerArgumentBinder
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a dictionary of the parameter-argument name-value pairs,
|
||||
/// Asyncronously binds a dictionary of the parameter-argument name-value pairs,
|
||||
/// which can be used to invoke the action. Also binds properties explicitly marked properties on the
|
||||
/// <paramref name="controller"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ControllerContext"/> associated with the current action.</param>
|
||||
/// <param name="controllerContext">The <see cref="ControllerContext"/> associated with the current action.</param>
|
||||
/// <param name="controller">The controller object which contains the action.</param>
|
||||
Task<IDictionary<string, object>> BindActionArgumentsAsync(ControllerContext context, object controller);
|
||||
/// <param name="arguments">The arguments dictionary.</param>
|
||||
/// <returns>A <see cref="Task"/> which, when completed signals the completion of argument binding.</returns>
|
||||
Task BindArgumentsAsync(
|
||||
ControllerContext controllerContext,
|
||||
object controller,
|
||||
IDictionary<string, object> arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());
|
||||
|
||||
// These are stateless
|
||||
services.TryAddSingleton<IControllerActionArgumentBinder, ControllerArgumentBinder>();
|
||||
services.TryAddSingleton<IControllerArgumentBinder, ControllerArgumentBinder>();
|
||||
services.TryAddSingleton<ControllerActionInvokerCache>();
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
private readonly ControllerActionDescriptor _descriptor;
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
private readonly IControllerActionArgumentBinder _argumentBinder;
|
||||
private readonly IControllerArgumentBinder _argumentBinder;
|
||||
|
||||
public ControllerActionInvoker(
|
||||
ActionContext actionContext,
|
||||
ControllerActionInvokerCache controllerActionInvokerCache,
|
||||
IControllerFactory controllerFactory,
|
||||
ControllerActionDescriptor descriptor,
|
||||
IControllerActionArgumentBinder argumentBinder,
|
||||
IControllerArgumentBinder argumentBinder,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
|
|
@ -111,9 +111,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return actionResult;
|
||||
}
|
||||
|
||||
protected override Task<IDictionary<string, object>> BindActionArgumentsAsync()
|
||||
protected override Task BindActionArgumentsAsync(IDictionary<string, object> arguments)
|
||||
{
|
||||
return _argumentBinder.BindActionArgumentsAsync(Context, Instance);
|
||||
if (arguments == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(arguments));
|
||||
}
|
||||
|
||||
return _argumentBinder.BindArgumentsAsync(Context, Instance, arguments);
|
||||
}
|
||||
|
||||
// Marking as internal for Unit Testing purposes.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
public class ControllerActionInvokerProvider : IActionInvokerProvider
|
||||
{
|
||||
private readonly IControllerActionArgumentBinder _argumentBinder;
|
||||
private readonly IControllerArgumentBinder _argumentBinder;
|
||||
private readonly IControllerFactory _controllerFactory;
|
||||
private readonly ControllerActionInvokerCache _controllerActionInvokerCache;
|
||||
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
|
||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
public ControllerActionInvokerProvider(
|
||||
IControllerFactory controllerFactory,
|
||||
ControllerActionInvokerCache controllerActionInvokerCache,
|
||||
IControllerActionArgumentBinder argumentBinder,
|
||||
IControllerArgumentBinder argumentBinder,
|
||||
IOptions<MvcOptions> optionsAccessor,
|
||||
ILoggerFactory loggerFactory,
|
||||
DiagnosticSource diagnosticSource)
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ using Microsoft.Extensions.Internal;
|
|||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a default implementation of <see cref="IControllerActionArgumentBinder"/>.
|
||||
/// Provides a default implementation of <see cref="IControllerArgumentBinder"/>.
|
||||
/// Uses ModelBinding to populate action parameters.
|
||||
/// </summary>
|
||||
public class ControllerArgumentBinder : IControllerActionArgumentBinder
|
||||
public class ControllerArgumentBinder : IControllerArgumentBinder
|
||||
{
|
||||
private static readonly MethodInfo CallPropertyAddRangeOpenGenericMethod =
|
||||
typeof(ControllerArgumentBinder).GetTypeInfo().GetDeclaredMethod(
|
||||
|
|
@ -38,9 +38,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
_validator = validator;
|
||||
}
|
||||
|
||||
public Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||
public Task BindArgumentsAsync(
|
||||
ControllerContext controllerContext,
|
||||
object controller)
|
||||
object controller,
|
||||
IDictionary<string, object> arguments)
|
||||
{
|
||||
if (controllerContext == null)
|
||||
{
|
||||
|
|
@ -65,53 +66,54 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
if (actionDescriptor.BoundProperties.Count == 0 &&
|
||||
actionDescriptor.Parameters.Count == 0)
|
||||
{
|
||||
return Task.FromResult<IDictionary<string, object>>(
|
||||
new Dictionary<string, object>(StringComparer.Ordinal));
|
||||
}
|
||||
else if (actionDescriptor.BoundProperties.Count == 0)
|
||||
{
|
||||
return BindActionArgumentsCoreAsync(controllerContext, actionDescriptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BindActionArgumentsAndPropertiesCoreAsync(
|
||||
controllerContext,
|
||||
controller,
|
||||
actionDescriptor);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
return BindArgumentsCoreAsync(controllerContext, controller, arguments);
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, object>> BindActionArgumentsCoreAsync(
|
||||
ControllerContext controllerContext,
|
||||
ControllerActionDescriptor actionDescriptor)
|
||||
{
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);
|
||||
|
||||
var actionArguments = await PopulateArgumentsAsync(
|
||||
controllerContext,
|
||||
actionDescriptor.Parameters,
|
||||
valueProvider);
|
||||
return actionArguments;
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, object>> BindActionArgumentsAndPropertiesCoreAsync(
|
||||
private async Task BindArgumentsCoreAsync(
|
||||
ControllerContext controllerContext,
|
||||
object controller,
|
||||
ControllerActionDescriptor actionDescriptor)
|
||||
IDictionary<string, object> arguments)
|
||||
{
|
||||
var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);
|
||||
|
||||
var controllerProperties = await PopulateArgumentsAsync(
|
||||
controllerContext,
|
||||
actionDescriptor.BoundProperties,
|
||||
valueProvider);
|
||||
ActivateProperties(actionDescriptor, controller, controllerProperties);
|
||||
var parameters = controllerContext.ActionDescriptor.Parameters;
|
||||
for (var i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
|
||||
var actionArguments = await PopulateArgumentsAsync(
|
||||
controllerContext,
|
||||
actionDescriptor.Parameters,
|
||||
valueProvider);
|
||||
return actionArguments;
|
||||
var result = await BindModelAsync(parameter, controllerContext, valueProvider);
|
||||
if (result != null && result.Value.IsModelSet)
|
||||
{
|
||||
arguments[parameter.Name] = result.Value.Model;
|
||||
}
|
||||
}
|
||||
|
||||
var properties = controllerContext.ActionDescriptor.BoundProperties;
|
||||
if (properties.Count == 0)
|
||||
{
|
||||
// Perf: Early exit to avoid PropertyHelper lookup in the (common) case where we have no
|
||||
// bound properties.
|
||||
return;
|
||||
}
|
||||
|
||||
var propertyHelpers = PropertyHelper.GetProperties(controller);
|
||||
for (var i = 0; i < properties.Count; i++)
|
||||
{
|
||||
var property = properties[i];
|
||||
|
||||
var result = await BindModelAsync(property, controllerContext, valueProvider);
|
||||
if (result != null && result.Value.IsModelSet)
|
||||
{
|
||||
var propertyHelper = FindPropertyHelper(propertyHelpers, property);
|
||||
if (propertyHelper != null)
|
||||
{
|
||||
ActivateProperty(property, propertyHelper, controller, result.Value.Model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ModelBindingResult?> BindModelAsync(
|
||||
|
|
@ -199,6 +201,52 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return modelBindingResult;
|
||||
}
|
||||
|
||||
private void ActivateProperty(
|
||||
ParameterDescriptor property,
|
||||
PropertyHelper propertyHelper,
|
||||
object controller,
|
||||
object value)
|
||||
{
|
||||
var propertyType = propertyHelper.Property.PropertyType;
|
||||
var metadata = _modelMetadataProvider.GetMetadataForType(propertyType);
|
||||
|
||||
if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPublic == true)
|
||||
{
|
||||
// Handle settable property. Do not set the property to null if the type is a non-nullable type.
|
||||
if (value != null || metadata.IsReferenceOrNullableType)
|
||||
{
|
||||
propertyHelper.SetValue(controller, value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyType.IsArray)
|
||||
{
|
||||
// Do not attempt to copy values into an array because an array's length is immutable. This choice
|
||||
// is also consistent with MutableObjectModelBinder's handling of a read-only array property.
|
||||
return;
|
||||
}
|
||||
|
||||
var target = propertyHelper.GetValue(controller);
|
||||
if (value == null || target == null)
|
||||
{
|
||||
// Nothing to do when source or target is null.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!metadata.IsCollectionType)
|
||||
{
|
||||
// Not a collection model.
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle a read-only collection property.
|
||||
var propertyAddRange = CallPropertyAddRangeOpenGenericMethod.MakeGenericMethod(
|
||||
metadata.ElementMetadata.ModelType);
|
||||
propertyAddRange.Invoke(obj: null, parameters: new[] { target, value });
|
||||
}
|
||||
|
||||
// Called via reflection.
|
||||
private static void CallPropertyAddRange<TElement>(object target, object source)
|
||||
{
|
||||
|
|
@ -214,92 +262,18 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private void ActivateProperties(
|
||||
ControllerActionDescriptor actionDescriptor,
|
||||
object controller,
|
||||
IDictionary<string, object> properties)
|
||||
private static PropertyHelper FindPropertyHelper(PropertyHelper[] propertyHelpers, ParameterDescriptor property)
|
||||
{
|
||||
var propertyHelpers = PropertyHelper.GetProperties(controller);
|
||||
for (var i = 0; i < actionDescriptor.BoundProperties.Count; i++)
|
||||
for (var i = 0; i < propertyHelpers.Length; i++)
|
||||
{
|
||||
var property = actionDescriptor.BoundProperties[i];
|
||||
|
||||
PropertyHelper propertyHelper = null;
|
||||
for (var j = 0; j < propertyHelpers.Length; j++)
|
||||
var propertyHelper = propertyHelpers[i];
|
||||
if (string.Equals(propertyHelper.Name, property.Name, StringComparison.Ordinal))
|
||||
{
|
||||
if (string.Equals(propertyHelpers[j].Name, property.Name, StringComparison.Ordinal))
|
||||
{
|
||||
propertyHelper = propertyHelpers[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
object value;
|
||||
if (propertyHelper == null || !properties.TryGetValue(property.Name, out value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyType = propertyHelper.Property.PropertyType;
|
||||
var metadata = _modelMetadataProvider.GetMetadataForType(propertyType);
|
||||
|
||||
if (propertyHelper.Property.CanWrite && propertyHelper.Property.SetMethod?.IsPublic == true)
|
||||
{
|
||||
// Handle settable property. Do not set the property to null if the type is a non-nullable type.
|
||||
if (value != null || metadata.IsReferenceOrNullableType)
|
||||
{
|
||||
propertyHelper.SetValue(controller, value);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyType.IsArray)
|
||||
{
|
||||
// Do not attempt to copy values into an array because an array's length is immutable. This choice
|
||||
// is also consistent with MutableObjectModelBinder's handling of a read-only array property.
|
||||
continue;
|
||||
}
|
||||
|
||||
var target = propertyHelper.GetValue(controller);
|
||||
if (value == null || target == null)
|
||||
{
|
||||
// Nothing to do when source or target is null.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!metadata.IsCollectionType)
|
||||
{
|
||||
// Not a collection model.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle a read-only collection property.
|
||||
var propertyAddRange = CallPropertyAddRangeOpenGenericMethod.MakeGenericMethod(
|
||||
metadata.ElementMetadata.ModelType);
|
||||
propertyAddRange.Invoke(obj: null, parameters: new[] { target, value });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, object>> PopulateArgumentsAsync(
|
||||
ControllerContext controllerContext,
|
||||
IList<ParameterDescriptor> parameters,
|
||||
IValueProvider valueProvider)
|
||||
{
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
// Perf: Avoid allocations
|
||||
for (var i = 0; i < parameters.Count; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
var modelBindingResult = await BindModelAsync(parameter, controllerContext, valueProvider);
|
||||
if (modelBindingResult != null && modelBindingResult.Value.IsModelSet)
|
||||
{
|
||||
arguments[parameter.Name] = modelBindingResult.Value.Model;
|
||||
return propertyHelper;
|
||||
}
|
||||
}
|
||||
|
||||
return arguments;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
||||
|
||||
protected abstract Task<IDictionary<string, object>> BindActionArgumentsAsync();
|
||||
protected abstract Task BindActionArgumentsAsync(IDictionary<string, object> arguments);
|
||||
|
||||
public virtual async Task InvokeAsync()
|
||||
{
|
||||
|
|
@ -456,13 +456,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
Instance = CreateInstance();
|
||||
|
||||
var arguments = await BindActionArgumentsAsync();
|
||||
|
||||
_actionExecutingContext = new ActionExecutingContext(
|
||||
Context,
|
||||
_filters,
|
||||
arguments,
|
||||
Instance);
|
||||
var arguments = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
await BindActionArgumentsAsync(arguments);
|
||||
_actionExecutingContext = new ActionExecutingContext(Context, _filters, arguments, Instance);
|
||||
|
||||
await InvokeActionFilterAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2037,10 +2037,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
.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>()));
|
||||
var argumentBinder = new Mock<IControllerArgumentBinder>();
|
||||
argumentBinder
|
||||
.Setup(b => b.BindArgumentsAsync(
|
||||
It.IsAny<ControllerContext>(),
|
||||
It.IsAny<object>(),
|
||||
It.IsAny<IDictionary<string, object>>()))
|
||||
.Returns(TaskCache.CompletedTask);
|
||||
|
||||
filterProvider
|
||||
.SetupGet(fp => fp.Order)
|
||||
|
|
@ -2051,7 +2054,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
new[] { filterProvider.Object },
|
||||
new MockControllerFactory(this),
|
||||
actionDescriptor,
|
||||
actionArgumentsBinder.Object,
|
||||
argumentBinder.Object,
|
||||
new IValueProviderFactory[0],
|
||||
new NullLoggerFactory().CreateLogger<ControllerActionInvoker>(),
|
||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||
|
|
@ -2227,7 +2230,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
IFilterProvider[] filterProviders,
|
||||
MockControllerFactory controllerFactory,
|
||||
ControllerActionDescriptor descriptor,
|
||||
IControllerActionArgumentBinder controllerActionArgumentBinder,
|
||||
IControllerArgumentBinder argumentBinder,
|
||||
IReadOnlyList<IValueProviderFactory> valueProviderFactories,
|
||||
ILogger logger,
|
||||
DiagnosticSource diagnosticSource,
|
||||
|
|
@ -2237,7 +2240,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
CreateFilterCache(filterProviders),
|
||||
controllerFactory,
|
||||
descriptor,
|
||||
controllerActionArgumentBinder,
|
||||
argumentBinder,
|
||||
valueProviderFactories,
|
||||
logger,
|
||||
diagnosticSource,
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
Assert.Empty(arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -68,12 +70,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
Assert.Empty(arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -106,13 +110,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, result.Count);
|
||||
Assert.Equal(value, result["foo"]);
|
||||
Assert.Equal(1, arguments.Count);
|
||||
Assert.Equal(value, arguments["foo"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -140,9 +146,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
It.IsAny<object>()));
|
||||
|
||||
var argumentBinder = GetArgumentBinder(factory, mockValidator.Object);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
mockValidator
|
||||
|
|
@ -169,6 +177,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var binder = new Mock<IModelBinder>();
|
||||
binder
|
||||
|
|
@ -184,10 +193,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
It.IsAny<object>()));
|
||||
|
||||
var factory = GetModelBinderFactory(binder.Object);
|
||||
var controller = new TestController();
|
||||
var argumentBinder = GetArgumentBinder(factory, mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
mockValidator
|
||||
|
|
@ -212,6 +222,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidator
|
||||
|
|
@ -225,7 +237,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var argumentBinder = GetArgumentBinder(factory, mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
mockValidator
|
||||
|
|
@ -251,6 +263,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var binder = new Mock<IModelBinder>();
|
||||
binder
|
||||
|
|
@ -269,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var argumentBinder = GetArgumentBinder(factory, mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
mockValidator
|
||||
|
|
@ -295,14 +309,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var factory = GetModelBinderFactory("Hello");
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controller = new TestController();
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Hello", controller.StringProperty);
|
||||
|
|
@ -324,15 +339,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var expected = new List<string> { "Hello", "World", "!!" };
|
||||
var factory = GetModelBinderFactory(expected);
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controller = new TestController();
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, controller.CollectionProperty);
|
||||
|
|
@ -356,18 +371,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var binder = new StubModelBinder(ModelBindingResult.Success(string.Empty, model: null));
|
||||
var factory = GetModelBinderFactory(binder);
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controller = new TestController();
|
||||
|
||||
// Some non default value.
|
||||
controller.NonNullableProperty = -1;
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(-1, controller.NonNullableProperty);
|
||||
|
|
@ -387,18 +403,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var binder = new StubModelBinder(ModelBindingResult.Success(key: string.Empty, model: null));
|
||||
var factory = GetModelBinderFactory(binder);
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controller = new TestController();
|
||||
|
||||
// Some non default value.
|
||||
controller.NullableProperty = -1;
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Null(controller.NullableProperty);
|
||||
|
|
@ -463,14 +480,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
});
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var factory = GetModelBinderFactory(inputValue);
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
|
||||
var controller = new TestController();
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedValue, propertyAccessor(controller));
|
||||
|
|
@ -520,6 +538,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
var controllerContext = GetControllerContext(actionDescriptor);
|
||||
var controller = new TestController();
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
var binder = new StubModelBinder(bindingContext =>
|
||||
{
|
||||
|
|
@ -540,10 +560,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory());
|
||||
|
||||
var argumentBinder = GetArgumentBinder(factory);
|
||||
var controller = new TestController();
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder.BindActionArgumentsAsync(controllerContext, controller);
|
||||
await argumentBinder.BindArgumentsAsync(controllerContext, controller, arguments);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new string[] { "goodbye" }, controller.ArrayProperty); // Skipped
|
||||
|
|
|
|||
|
|
@ -99,10 +99,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
},
|
||||
actionDescriptor: actionDescriptor);
|
||||
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var arguments = await argumentBinder.BindActionArgumentsAsync(testContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(testContext, new TestController(), arguments);
|
||||
|
||||
// Assert
|
||||
Assert.False(modelState.IsValid);
|
||||
|
|
@ -138,10 +139,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
},
|
||||
actionDescriptor: actionDescriptor);
|
||||
|
||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
var modelState = testContext.ModelState;
|
||||
|
||||
// Act
|
||||
var arguments = await argumentBinder.BindActionArgumentsAsync(testContext, new TestController());
|
||||
await argumentBinder.BindArgumentsAsync(testContext, new TestController(), arguments);
|
||||
|
||||
// Assert
|
||||
Assert.True(modelState.IsValid);
|
||||
|
|
|
|||
Loading…
Reference in New Issue