diff --git a/Mvc.NoFun.sln b/Mvc.NoFun.sln
index cab458c988..166b89b514 100644
--- a/Mvc.NoFun.sln
+++ b/Mvc.NoFun.sln
@@ -28,6 +28,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FAD65E9C-3CF3-4F68-9757-C7358604030B}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
+ NuGet.config = NuGet.config
NuGetPackageVerifier.json = NuGetPackageVerifier.json
EndProjectSection
EndProject
diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/IModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/IModelBinder.cs
index 834886d712..948af2f376 100644
--- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/IModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/IModelBinder.cs
@@ -24,9 +24,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// with . If model binding failed, the
/// should be a value created with .
/// If there was no data, or this model binder cannot handle the operation, the
- /// should be .
+ /// should be null.
///
///
- Task BindModelAsync(ModelBindingContext bindingContext);
+ Task BindModelAsync(ModelBindingContext bindingContext);
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingContext.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingContext.cs
index 7530c5fcf9..35690dc1e1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingContext.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingContext.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -9,248 +9,24 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
/// A context that contains operating information for model binding and validation.
///
- public class ModelBindingContext
+ public abstract class ModelBindingContext
{
- private string _fieldName;
- private ModelMetadata _modelMetadata;
- private string _modelName;
- private ModelStateDictionary _modelState;
- private OperationBindingContext _operationBindingContext;
- private IValueProvider _valueProvider;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ModelBindingContext()
- {
- }
-
- ///
- /// Creates a new for top-level model binding operation.
- ///
- ///
- /// The associated with the binding operation.
- ///
- /// associated with the model.
- /// associated with the model.
- /// The name of the property or parameter being bound.
- /// A new instance of .
- public static ModelBindingContext CreateBindingContext(
- OperationBindingContext operationBindingContext,
- ModelMetadata metadata,
- BindingInfo bindingInfo,
- string modelName)
- {
- if (operationBindingContext == null)
- {
- throw new ArgumentNullException(nameof(operationBindingContext));
- }
-
- if (metadata == null)
- {
- throw new ArgumentNullException(nameof(metadata));
- }
-
- if (modelName == null)
- {
- throw new ArgumentNullException(nameof(modelName));
- }
-
- var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName;
- var propertyPredicateProvider =
- bindingInfo?.PropertyBindingPredicateProvider ?? metadata.PropertyBindingPredicateProvider;
-
- return new ModelBindingContext()
- {
- BinderModelName = binderModelName,
- BindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource,
- BinderType = bindingInfo?.BinderType ?? metadata.BinderType,
- PropertyFilter = propertyPredicateProvider?.PropertyFilter,
-
- // We only support fallback to empty prefix in cases where the model name is inferred from
- // the parameter or property being bound.
- FallbackToEmptyPrefix = binderModelName == null,
-
- // Because this is the top-level context, FieldName and ModelName should be the same.
- FieldName = binderModelName ?? modelName,
- ModelName = binderModelName ?? modelName,
-
- IsTopLevelObject = true,
- ModelMetadata = metadata,
- ModelState = operationBindingContext.ActionContext.ModelState,
- OperationBindingContext = operationBindingContext,
- ValueProvider = operationBindingContext.ValueProvider,
-
- ValidationState = new ValidationStateDictionary(),
- };
- }
-
- public static ModelBindingContext CreateChildBindingContext(
- ModelBindingContext parent,
- ModelMetadata modelMetadata,
- string fieldName,
- string modelName,
- object model)
- {
- if (parent == null)
- {
- throw new ArgumentNullException(nameof(parent));
- }
-
- if (modelMetadata == null)
- {
- throw new ArgumentNullException(nameof(modelMetadata));
- }
-
- if (fieldName == null)
- {
- throw new ArgumentNullException(nameof(fieldName));
- }
-
- if (modelName == null)
- {
- throw new ArgumentNullException(nameof(modelName));
- }
-
- return new ModelBindingContext()
- {
- ModelState = parent.ModelState,
- OperationBindingContext = parent.OperationBindingContext,
- ValueProvider = parent.ValueProvider,
- ValidationState = parent.ValidationState,
-
- Model = model,
- ModelMetadata = modelMetadata,
- ModelName = modelName,
- FieldName = fieldName,
- BinderModelName = modelMetadata.BinderModelName,
- BinderType = modelMetadata.BinderType,
- BindingSource = modelMetadata.BindingSource,
- PropertyFilter = modelMetadata.PropertyBindingPredicateProvider?.PropertyFilter,
- };
- }
-
- ///
- /// Represents the associated with this context.
- ///
- public OperationBindingContext OperationBindingContext
- {
- get { return _operationBindingContext; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _operationBindingContext = value;
- }
- }
-
- ///
- /// Gets or sets the name of the current field being bound.
- ///
- public string FieldName
- {
- get { return _fieldName; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _fieldName = value;
- }
- }
-
- ///
- /// Gets or sets the model value for the current operation.
- ///
- ///
- /// The will typically be set for a binding operation that works
- /// against a pre-existing model object to update certain properties.
- ///
- public object Model { get; set; }
-
- ///
- /// Gets or sets the metadata for the model associated with this context.
- ///
- public ModelMetadata ModelMetadata
- {
- get { return _modelMetadata; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _modelMetadata = value;
- }
- }
-
- ///
- /// Gets or sets the name of the model. This property is used as a key for looking up values in
- /// during model binding.
- ///
- public string ModelName
- {
- get { return _modelName; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _modelName = value;
- }
- }
-
- ///
- /// Gets or sets the used to capture values
- /// for properties in the object graph of the model when binding.
- ///
- public ModelStateDictionary ModelState
- {
- get { return _modelState; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _modelState = value;
- }
- }
-
- ///
- /// Gets the type of the model.
- ///
- ///
- /// The property must be set to access this property.
- ///
- public Type ModelType => ModelMetadata?.ModelType;
-
///
/// Gets or sets a model name which is explicitly set using an .
- /// .
///
- public string BinderModelName { get; set; }
-
- ///
- /// Gets or sets a value which represents the associated with the
- /// .
- ///
- public BindingSource BindingSource { get; set; }
+ public abstract string BinderModelName { get; set; }
///
/// Gets the of an associated with the
/// .
///
- public Type BinderType { get; set; }
+ public abstract Type BinderType { get; set; }
+
+ ///
+ /// Gets or sets a value which represents the associated with the
+ /// .
+ ///
+ public abstract BindingSource BindingSource { get; set; }
///
/// Gets or sets a value that indicates whether the binder should use an empty prefix to look up
@@ -260,41 +36,138 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// Passed into the model binding system. Should not be true when is
/// false.
///
- public bool FallbackToEmptyPrefix { get; set; }
+ public abstract bool FallbackToEmptyPrefix { get; set; }
+
+ ///
+ /// Gets or sets the name of the current field being bound.
+ ///
+ public abstract string FieldName { get; set; }
///
/// Gets or sets an indication that the current binder is handling the top-level object.
///
/// Passed into the model binding system.
- public bool IsTopLevelObject { get; set; }
+ public abstract bool IsTopLevelObject { get; set; }
///
- /// Gets or sets the associated with this context.
+ /// Gets or sets the model value for the current operation.
///
- public IValueProvider ValueProvider
- {
- get { return _valueProvider; }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
+ ///
+ /// The will typically be set for a binding operation that works
+ /// against a pre-existing model object to update certain properties.
+ ///
+ public abstract object Model { get; set; }
- _valueProvider = value;
- }
- }
+ ///
+ /// Gets or sets the metadata for the model associated with this context.
+ ///
+ public abstract ModelMetadata ModelMetadata { get; set; }
+
+ ///
+ /// Gets or sets the name of the model. This property is used as a key for looking up values in
+ /// during model binding.
+ ///
+ public abstract string ModelName { get; set; }
+
+ ///
+ /// Gets or sets the used to capture values
+ /// for properties in the object graph of the model when binding.
+ ///
+ public abstract ModelStateDictionary ModelState { get; set; }
+
+ ///
+ /// Gets the type of the model.
+ ///
+ ///
+ /// The property must be set to access this property.
+ ///
+ public abstract Type ModelType { get; }
+
+ ///
+ /// Represents the associated with this context.
+ ///
+ public abstract OperationBindingContext OperationBindingContext { get; set; }
///
/// Gets or sets a predicate which will be evaluated for each property to determine if the property
/// is eligible for model binding.
///
- public Func PropertyFilter { get; set; }
+ public abstract Func PropertyFilter { get; set; }
///
/// Gets or sets the . Used for tracking validation state to
/// customize validation behavior for a model object.
///
- public ValidationStateDictionary ValidationState { get; set; }
+ public abstract ValidationStateDictionary ValidationState { get; set; }
+
+ ///
+ /// Gets or sets the associated with this context.
+ ///
+ public abstract IValueProvider ValueProvider { get; set; }
+
+ ///
+ ///
+ /// On completion returns a which
+ /// represents the result of the model binding process.
+ ///
+ ///
+ /// If model binding was successful, the should be a value created
+ /// with . If model binding failed, the
+ /// should be a value created with .
+ /// If there was no data, or this model binder cannot handle the operation, the
+ /// should be null.
+ ///
+ ///
+ public abstract ModelBindingResult? Result { get; set; }
+
+ ///
+ /// Pushes a layer of state onto this context. Model binders will call this as part of recursion when binding properties
+ /// or collection items.
+ ///
+ /// to assign to the property.
+ /// Name to assign to the property.
+ /// Name to assign to the property.
+ /// Instance to assign to the property.
+ /// A scope object which should be used in a using statement where PushContext is called.
+ public abstract NestedScope EnterNestedScope(ModelMetadata modelMetadata, string fieldName, string modelName, object model);
+
+ ///
+ /// Pushes a layer of state onto this context. Model binders will call this as part of recursion when binding properties
+ /// or collection items.
+ ///
+ /// A scope object which should be used in a using statement where PushContext is called.
+ public abstract NestedScope EnterNestedScope();
+
+ ///
+ /// Removes a layer of state pushed by calling .
+ ///
+ protected abstract void ExitNestedScope();
+
+ ///
+ /// Return value of . Should be disposed
+ /// by caller when child binding context state should be popped off of
+ /// the .
+ ///
+ public struct NestedScope : IDisposable
+ {
+ private readonly ModelBindingContext _context;
+
+ ///
+ /// Initializes the for a .
+ ///
+ ///
+ public NestedScope(ModelBindingContext context)
+ {
+ _context = context;
+ }
+
+ ///
+ /// Exits the created by calling .
+ ///
+ public void Dispose()
+ {
+ _context.ExitNestedScope();
+ }
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingResult.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingResult.cs
index 7ea7457dd3..72f63cc851 100644
--- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingResult.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/ModelBindingResult.cs
@@ -12,18 +12,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
public struct ModelBindingResult : IEquatable
{
- ///
- /// A representing the lack of a result. The model binding
- /// system will continue to execute other model binders.
- ///
- public static readonly ModelBindingResult NoResult = new ModelBindingResult();
-
- ///
- /// Returns a completed representing the lack of a result. The model
- /// binding system will continue to execute other model binders.
- ///
- public static readonly Task NoResultAsync = Task.FromResult(NoResult);
-
///
/// Creates a representing a failed model binding operation.
///
@@ -38,22 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return new ModelBindingResult(key, model: null, isModelSet: false);
}
-
- ///
- /// Creates a completed representing a failed model binding operation.
- ///
- /// The key of the current model binding operation.
- /// A completed representing a failed model binding operation.
- public static Task FailedAsync(string key)
- {
- if (key == null)
- {
- throw new ArgumentNullException(nameof(key));
- }
-
- return Task.FromResult(Failed(key));
- }
-
+
///
/// Creates a representing a successful model binding operation.
///
@@ -72,25 +45,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
return new ModelBindingResult(key, model, isModelSet: true);
}
- ///
- /// Creates a completed representing a successful model binding
- /// operation.
- ///
- /// The key of the current model binding operation.
- /// The model value. May be null.
- /// A completed representing a successful model bind.
- public static Task SuccessAsync(
- string key,
- object model)
- {
- if (key == null)
- {
- throw new ArgumentNullException(nameof(key));
- }
-
- return Task.FromResult(Success(key, model));
- }
-
private ModelBindingResult(string key, object model, bool isModelSet)
{
Key = key;
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerArgumentBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerArgumentBinder.cs
index d632b1f68e..89408ec0e1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerArgumentBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerArgumentBinder.cs
@@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return actionArguments;
}
- public async Task BindModelAsync(
+ public async Task BindModelAsync(
ParameterDescriptor parameter,
OperationBindingContext operationContext)
{
@@ -112,21 +112,22 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
- var modelBindingContext = ModelBindingContext.CreateBindingContext(
+ var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
operationContext,
metadata,
parameter.BindingInfo,
parameter.Name);
- var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
- if (modelBindingResult.IsModelSet)
+ await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
+ var modelBindingResult = modelBindingContext.Result;
+ if (modelBindingResult != null && modelBindingResult.Value.IsModelSet)
{
_validator.Validate(
operationContext.ActionContext,
operationContext.ValidatorProvider,
modelBindingContext.ValidationState,
- modelBindingResult.Key,
- modelBindingResult.Model);
+ modelBindingResult.Value.Key,
+ modelBindingResult.Value.Model);
}
return modelBindingResult;
@@ -225,9 +226,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
{
var parameter = parameterMetadata[i];
var modelBindingResult = await BindModelAsync(parameter, operationContext);
- if (modelBindingResult.IsModelSet)
+ if (modelBindingResult != null && modelBindingResult.Value.IsModelSet)
{
- arguments[parameter.Name] = modelBindingResult.Model;
+ arguments[parameter.Name] = modelBindingResult.Value.Model;
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultModelBindingContext.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultModelBindingContext.cs
new file mode 100644
index 0000000000..5a2027bfb2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultModelBindingContext.cs
@@ -0,0 +1,323 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
+
+namespace Microsoft.AspNetCore.Mvc.ModelBinding
+{
+ ///
+ /// A context that contains operating information for model binding and validation.
+ ///
+ public class DefaultModelBindingContext : ModelBindingContext
+ {
+ private State _state;
+ private readonly Stack _stack = new Stack();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DefaultModelBindingContext()
+ {
+ }
+
+ ///
+ /// Creates a new for top-level model binding operation.
+ ///
+ ///
+ /// The associated with the binding operation.
+ ///
+ /// associated with the model.
+ /// associated with the model.
+ /// The name of the property or parameter being bound.
+ /// A new instance of .
+ public static ModelBindingContext CreateBindingContext(
+ OperationBindingContext operationBindingContext,
+ ModelMetadata metadata,
+ BindingInfo bindingInfo,
+ string modelName)
+ {
+ if (operationBindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(operationBindingContext));
+ }
+
+ if (metadata == null)
+ {
+ throw new ArgumentNullException(nameof(metadata));
+ }
+
+ if (modelName == null)
+ {
+ throw new ArgumentNullException(nameof(modelName));
+ }
+
+ var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName;
+ var propertyPredicateProvider =
+ bindingInfo?.PropertyBindingPredicateProvider ?? metadata.PropertyBindingPredicateProvider;
+
+ return new DefaultModelBindingContext()
+ {
+ BinderModelName = binderModelName,
+ BindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource,
+ BinderType = bindingInfo?.BinderType ?? metadata.BinderType,
+ PropertyFilter = propertyPredicateProvider?.PropertyFilter,
+
+ // We only support fallback to empty prefix in cases where the model name is inferred from
+ // the parameter or property being bound.
+ FallbackToEmptyPrefix = binderModelName == null,
+
+ // Because this is the top-level context, FieldName and ModelName should be the same.
+ FieldName = binderModelName ?? modelName,
+ ModelName = binderModelName ?? modelName,
+
+ IsTopLevelObject = true,
+ ModelMetadata = metadata,
+ ModelState = operationBindingContext.ActionContext.ModelState,
+ OperationBindingContext = operationBindingContext,
+ ValueProvider = operationBindingContext.ValueProvider,
+
+ ValidationState = new ValidationStateDictionary(),
+ };
+ }
+
+ ///
+ public override NestedScope EnterNestedScope(
+ ModelMetadata modelMetadata,
+ string fieldName,
+ string modelName,
+ object model)
+ {
+ if (modelMetadata == null)
+ {
+ throw new ArgumentNullException(nameof(modelMetadata));
+ }
+
+ if (fieldName == null)
+ {
+ throw new ArgumentNullException(nameof(fieldName));
+ }
+
+ if (modelName == null)
+ {
+ throw new ArgumentNullException(nameof(modelName));
+ }
+
+ var scope = EnterNestedScope();
+
+ Model = model;
+ ModelMetadata = modelMetadata;
+ ModelName = modelName;
+ FieldName = fieldName;
+ BinderModelName = modelMetadata.BinderModelName;
+ BinderType = modelMetadata.BinderType;
+ BindingSource = modelMetadata.BindingSource;
+ PropertyFilter = modelMetadata.PropertyBindingPredicateProvider?.PropertyFilter;
+
+ FallbackToEmptyPrefix = false;
+ IsTopLevelObject = false;
+
+ return scope;
+ }
+
+ ///
+ public override NestedScope EnterNestedScope()
+ {
+ _stack.Push(_state);
+
+ Result = null;
+
+ return new NestedScope(this);
+ }
+
+ ///
+ protected override void ExitNestedScope()
+ {
+ _state = _stack.Pop();
+ }
+
+ ///
+ public override OperationBindingContext OperationBindingContext
+ {
+ get { return _state.OperationBindingContext; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.OperationBindingContext = value;
+ }
+ }
+
+ ///
+ public override string FieldName
+ {
+ get { return _state.FieldName; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.FieldName = value;
+ }
+ }
+
+ ///
+ public override object Model
+ {
+ get { return _state.Model; }
+ set { _state.Model = value; }
+ }
+
+ ///
+ public override ModelMetadata ModelMetadata
+ {
+ get { return _state.ModelMetadata; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.ModelMetadata = value;
+ }
+ }
+
+ ///
+ public override string ModelName
+ {
+ get { return _state.ModelName; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.ModelName = value;
+ }
+ }
+
+ ///
+ public override ModelStateDictionary ModelState
+ {
+ get { return _state.ModelState; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.ModelState = value;
+ }
+ }
+
+ ///
+ public override Type ModelType => ModelMetadata?.ModelType;
+
+ ///
+ public override string BinderModelName
+ {
+ get { return _state.BinderModelName; }
+ set { _state.BinderModelName = value; }
+ }
+
+ ///
+ public override BindingSource BindingSource
+ {
+ get { return _state.BindingSource; }
+ set { _state.BindingSource = value; }
+ }
+
+ ///
+ public override Type BinderType
+ {
+ get { return _state.BinderType; }
+ set { _state.BinderType = value; }
+ }
+
+ ///
+ public override bool FallbackToEmptyPrefix
+ {
+ get { return _state.FallbackToEmptyPrefix; }
+ set { _state.FallbackToEmptyPrefix = value; }
+ }
+
+ ///
+ public override bool IsTopLevelObject
+ {
+ get { return _state.IsTopLevelObject; }
+ set { _state.IsTopLevelObject = value; }
+ }
+
+ ///
+ public override IValueProvider ValueProvider
+ {
+ get { return _state.ValueProvider; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ _state.ValueProvider = value;
+ }
+ }
+
+ ///
+ public override Func PropertyFilter
+ {
+ get { return _state.PropertyFilter; }
+ set { _state.PropertyFilter = value; }
+ }
+
+ ///
+ public override ValidationStateDictionary ValidationState
+ {
+ get { return _state.ValidationState; }
+ set { _state.ValidationState = value; }
+ }
+
+ ///
+ public override ModelBindingResult? Result
+ {
+ get
+ {
+ return _state.Result;
+ }
+ set
+ {
+ if (value.HasValue && value.Value == default(ModelBindingResult))
+ {
+ throw new ArgumentException(nameof(ModelBindingResult));
+ }
+
+ _state.Result = value;
+ }
+ }
+
+ private struct State
+ {
+ public OperationBindingContext OperationBindingContext;
+ public string FieldName;
+ public object Model;
+ public ModelMetadata ModelMetadata;
+ public string ModelName;
+
+ public IValueProvider ValueProvider;
+ public Func PropertyFilter;
+ public ValidationStateDictionary ValidationState;
+ public ModelStateDictionary ModelState;
+
+ public string BinderModelName;
+ public BindingSource BindingSource;
+ public Type BinderType;
+ public bool FallbackToEmptyPrefix;
+ public bool IsTopLevelObject;
+
+ public ModelBindingResult? Result;
+ };
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ArrayModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ArrayModelBinder.cs
index 1c2a55ea28..a0d092a2b3 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ArrayModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ArrayModelBinder.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
@@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class ArrayModelBinder : CollectionModelBinder
{
///
- public override Task BindModelAsync(ModelBindingContext bindingContext)
+ public override Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (bindingContext.ModelMetadata.IsReadOnly)
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return base.BindModelAsync(bindingContext);
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BinderTypeBasedModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BinderTypeBasedModelBinder.cs
index a62f86b59a..3e2676931c 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BinderTypeBasedModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BinderTypeBasedModelBinder.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
@@ -21,23 +22,28 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private readonly ConcurrentDictionary _typeActivatorCache =
new ConcurrentDictionary();
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
if (bindingContext.BinderType == null)
{
- // Return NoResult so that we are able to continue with the default set of model binders,
+ // Return null so that we are able to continue with the default set of model binders,
// if there is no specific model binder provided.
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext);
}
- private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
+ private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
{
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
var createFactory = _typeActivatorCache.GetOrAdd(bindingContext.BinderType, _createFactory);
@@ -52,15 +58,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
typeof(IModelBinder).FullName));
}
- var result = await modelBinder.BindModelAsync(bindingContext);
-
- var modelBindingResult = result != ModelBindingResult.NoResult ?
- result :
- ModelBindingResult.Failed(bindingContext.ModelName);
+ await modelBinder.BindModelAsync(bindingContext);
// A model binder was specified by metadata and this binder handles all such cases.
// Always tell the model binding system to skip other model binders i.e. return non-null.
- return modelBindingResult;
+ if (bindingContext.Result == null)
+ {
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ }
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BodyModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BodyModelBinder.cs
index f854657dd1..e9b673cad7 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BodyModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BodyModelBinder.cs
@@ -33,8 +33,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
}
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
@@ -45,7 +50,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// Formatters are opt-in. This model either didn't specify [FromBody] or specified something
// incompatible so let other binders run.
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext);
@@ -58,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
/// A which when completed returns a .
///
- private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
+ private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -101,7 +106,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// This model binder is the only handler for the Body binding source and it cannot run twice. Always
// tell the model binding system to skip other model binders and never to fall back i.e. indicate a
// fatal error.
- return ModelBindingResult.Failed(modelBindingKey);
+ bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
+ return;
}
try
@@ -114,10 +120,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// Formatter encountered an error. Do not use the model it returned. As above, tell the model
// binding system to skip other model binders and never to fall back.
- return ModelBindingResult.Failed(modelBindingKey);
+ bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
+ return;
}
- return ModelBindingResult.Success(modelBindingKey, model);
+ bindingContext.Result = ModelBindingResult.Success(modelBindingKey, model);
+ return;
}
catch (Exception ex)
{
@@ -126,7 +134,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// This model binder is the only handler for the Body binding source and it cannot run twice. Always
// tell the model binding system to skip other model binders and never to fall back i.e. indicate a
// fatal error.
- return ModelBindingResult.Failed(modelBindingKey);
+ bindingContext.Result = ModelBindingResult.Failed(modelBindingKey);
+ return;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs
index 399e6fc3a7..ad22b699e7 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs
@@ -3,6 +3,7 @@
using System;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
@@ -12,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class ByteArrayModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -26,14 +27,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Check if this binder applies.
if (bindingContext.ModelType != typeof(byte[]))
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
// Check for missing data case 1: There was no element containing this data.
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
@@ -42,13 +44,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value))
{
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
try
{
var model = Convert.FromBase64String(value);
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return TaskCache.CompletedTask;
}
catch (Exception exception)
{
@@ -60,7 +64,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Matched the type (byte[]) only this binder supports. As in missing data cases, always tell the model
// binding system to skip other model binders i.e. return non-null.
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CancellationTokenModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CancellationTokenModelBinder.cs
index 0b0097e254..560a6831ab 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CancellationTokenModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CancellationTokenModelBinder.cs
@@ -1,9 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
@@ -13,8 +15,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class CancellationTokenModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
if (bindingContext.ModelType == typeof(CancellationToken))
{
// We need to force boxing now, so we can insert the same reference to the boxed CancellationToken
@@ -23,10 +30,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// DO NOT simplify this code by removing the cast.
var model = (object)bindingContext.OperationBindingContext.HttpContext.RequestAborted;
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
}
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CollectionModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CollectionModelBinder.cs
index ead04b7c9f..2d261f92cb 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CollectionModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CollectionModelBinder.cs
@@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class CollectionModelBinder : ICollectionModelBinder
{
///
- public virtual async Task BindModelAsync(ModelBindingContext bindingContext)
+ public virtual async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -44,10 +44,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
model = CreateEmptyCollection(bindingContext.ModelType);
}
- return ModelBindingResult.Success(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return;
}
- return ModelBindingResult.NoResult;
+ return;
}
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
@@ -91,7 +92,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
valueProviderResult);
}
- return ModelBindingResult.Success(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return;
}
///
@@ -147,16 +149,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
- var innerBindingContext = ModelBindingContext.CreateChildBindingContext(
- bindingContext,
- elementMetadata,
- fieldName: bindingContext.FieldName,
- modelName: bindingContext.ModelName,
- model: null);
foreach (var value in values)
{
- innerBindingContext.ValueProvider = new CompositeValueProvider
+ bindingContext.ValueProvider = new CompositeValueProvider
{
// our temporary provider goes at the front of the list
new ElementalValueProvider(bindingContext.ModelName, value, values.Culture),
@@ -164,12 +160,20 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
};
object boundValue = null;
- var result =
- await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(innerBindingContext);
- if (result != null && result.IsModelSet)
+
+ using (bindingContext.EnterNestedScope(
+ elementMetadata,
+ fieldName: bindingContext.FieldName,
+ modelName: bindingContext.ModelName,
+ model: null))
{
- boundValue = result.Model;
- boundCollection.Add(ModelBindingHelper.CastOrDefault(boundValue));
+ await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);
+
+ if (bindingContext.Result != null && bindingContext.Result.Value.IsModelSet)
+ {
+ boundValue = bindingContext.Result.Value.Model;
+ boundCollection.Add(ModelBindingHelper.CastOrDefault(boundValue));
+ }
}
}
@@ -214,23 +218,24 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
foreach (var indexName in indexNames)
{
var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName);
- var childBindingContext = ModelBindingContext.CreateChildBindingContext(
- bindingContext,
- elementMetadata,
- fieldName: indexName,
- modelName: fullChildName,
- model: null);
-
var didBind = false;
object boundValue = null;
+ ModelBindingResult? result;
+ using (bindingContext.EnterNestedScope(
+ elementMetadata,
+ fieldName: indexName,
+ modelName: fullChildName,
+ model: null))
+ {
+ await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);
+ result = bindingContext.Result;
+ }
- var result =
- await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childBindingContext);
- if (result != null && result.IsModelSet)
+ if (result != null && result.Value.IsModelSet)
{
didBind = true;
- boundValue = result.Model;
+ boundValue = result.Value.Model;
}
// infinite size collection stops on first bind failure
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeModelBinder.cs
index c46f914720..aa9b2b51f1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/CompositeModelBinder.cs
@@ -36,70 +36,78 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
///
public IList ModelBinders { get; }
- public virtual Task BindModelAsync(ModelBindingContext bindingContext)
+ public virtual Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
- var newBindingContext = CreateNewBindingContext(bindingContext);
- if (newBindingContext == null)
- {
- // Unable to find a value provider for this binding source. Binding will fail.
- return ModelBindingResult.NoResultAsync;
- }
-
- return RunModelBinders(newBindingContext);
+ return RunModelBinders(bindingContext);
}
- private async Task RunModelBinders(ModelBindingContext bindingContext)
+ private async Task RunModelBinders(ModelBindingContext bindingContext)
{
RuntimeHelpers.EnsureSufficientExecutionStack();
- // Perf: Avoid allocations
- for (var i = 0; i < ModelBinders.Count; i++)
+ ModelBindingResult? overallResult = null;
+ try
{
- var binder = ModelBinders[i];
- var result = await binder.BindModelAsync(bindingContext);
- if (result != ModelBindingResult.NoResult)
+ using (bindingContext.EnterNestedScope())
{
- // This condition is necessary because the ModelState entry would never be validated if
- // caller fell back to the empty prefix, leading to an possibly-incorrect !IsValid. In most
- // (hopefully all) cases, the ModelState entry exists because some binders add errors before
- // returning a result with !IsModelSet. Those binders often cannot run twice anyhow.
- if (result.IsModelSet ||
- bindingContext.ModelState.ContainsKey(bindingContext.ModelName))
+ if (PrepareBindingContext(bindingContext))
{
- if (bindingContext.IsTopLevelObject && result.Model != null)
+ // Perf: Avoid allocations
+ for (var i = 0; i < ModelBinders.Count; i++)
{
- ValidationStateEntry entry;
- if (!bindingContext.ValidationState.TryGetValue(result.Model, out entry))
+ var binder = ModelBinders[i];
+ await binder.BindModelAsync(bindingContext);
+ if (bindingContext.Result != null)
{
- entry = new ValidationStateEntry()
+ var result = bindingContext.Result.Value;
+ // This condition is necessary because the ModelState entry would never be validated if
+ // caller fell back to the empty prefix, leading to an possibly-incorrect !IsValid. In most
+ // (hopefully all) cases, the ModelState entry exists because some binders add errors before
+ // returning a result with !IsModelSet. Those binders often cannot run twice anyhow.
+ if (result.IsModelSet ||
+ bindingContext.ModelState.ContainsKey(bindingContext.ModelName))
{
- Key = result.Key,
- Metadata = bindingContext.ModelMetadata,
- };
- bindingContext.ValidationState.Add(result.Model, entry);
+ if (bindingContext.IsTopLevelObject && result.Model != null)
+ {
+ ValidationStateEntry entry;
+ if (!bindingContext.ValidationState.TryGetValue(result.Model, out entry))
+ {
+ entry = new ValidationStateEntry()
+ {
+ Key = result.Key,
+ Metadata = bindingContext.ModelMetadata,
+ };
+ bindingContext.ValidationState.Add(result.Model, entry);
+ }
+ }
+
+ overallResult = bindingContext.Result;
+ return;
+ }
+
+ // Current binder should have been able to bind value but found nothing. Exit loop in a way that
+ // tells caller to fall back to the empty prefix, if appropriate. Do not return result because it
+ // means only "other binders are not applicable".
+
+ // overallResult MUST still be null at this return statement.
+ return;
}
}
-
- return result;
}
-
- // Current binder should have been able to bind value but found nothing. Exit loop in a way that
- // tells caller to fall back to the empty prefix, if appropriate. Do not return result because it
- // means only "other binders are not applicable".
- break;
}
}
-
- // Either we couldn't find a binder, or the binder couldn't bind. Distinction is not important.
- return ModelBindingResult.NoResult;
+ finally
+ {
+ bindingContext.Result = overallResult;
+ }
}
- private static ModelBindingContext CreateNewBindingContext(ModelBindingContext oldBindingContext)
+ private static bool PrepareBindingContext(ModelBindingContext bindingContext)
{
// If the property has a specified data binding sources, we need to filter the set of value providers
// to just those that match. We can skip filtering when IsGreedy == true, because that can't use
@@ -119,8 +127,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// public IActionResult UpdatePerson([FromForm] Person person) { }
//
// In this example, [FromQuery] overrides the ambient data source (form).
- IValueProvider valueProvider = oldBindingContext.ValueProvider;
- var bindingSource = oldBindingContext.BindingSource;
+
+ var valueProvider = bindingContext.ValueProvider;
+ var bindingSource = bindingContext.BindingSource;
+ var modelName = bindingContext.ModelName;
+ var fallbackToEmptyPrefix = bindingContext.FallbackToEmptyPrefix;
+
if (bindingSource != null && !bindingSource.IsGreedy)
{
var bindingSourceValueProvider = valueProvider as IBindingSourceValueProvider;
@@ -130,43 +142,30 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (valueProvider == null)
{
// Unable to find a value provider for this binding source.
- return null;
+ return false;
}
}
}
- var newBindingContext = new ModelBindingContext
- {
- Model = oldBindingContext.Model,
- ModelMetadata = oldBindingContext.ModelMetadata,
- FieldName = oldBindingContext.FieldName,
- ModelState = oldBindingContext.ModelState,
- ValueProvider = valueProvider,
- OperationBindingContext = oldBindingContext.OperationBindingContext,
- PropertyFilter = oldBindingContext.PropertyFilter,
- BinderModelName = oldBindingContext.BinderModelName,
- BindingSource = oldBindingContext.BindingSource,
- BinderType = oldBindingContext.BinderType,
- IsTopLevelObject = oldBindingContext.IsTopLevelObject,
- ValidationState = oldBindingContext.ValidationState,
- };
-
if (bindingSource != null && bindingSource.IsGreedy)
{
- newBindingContext.ModelName = oldBindingContext.ModelName;
+ bindingContext.ModelName = modelName;
}
else if (
- !oldBindingContext.FallbackToEmptyPrefix ||
- newBindingContext.ValueProvider.ContainsPrefix(oldBindingContext.ModelName))
+ !fallbackToEmptyPrefix ||
+ valueProvider.ContainsPrefix(bindingContext.ModelName))
{
- newBindingContext.ModelName = oldBindingContext.ModelName;
+ bindingContext.ModelName = modelName;
}
else
{
- newBindingContext.ModelName = string.Empty;
+ bindingContext.ModelName = string.Empty;
}
- return newBindingContext;
+ bindingContext.ValueProvider = valueProvider;
+ bindingContext.FallbackToEmptyPrefix = false;
+
+ return true;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/DictionaryModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/DictionaryModelBinder.cs
index 21dcb61578..47f7800323 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/DictionaryModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/DictionaryModelBinder.cs
@@ -22,26 +22,28 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class DictionaryModelBinder : CollectionModelBinder>
{
///
- public override async Task BindModelAsync(ModelBindingContext bindingContext)
+ public override async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
- var result = await base.BindModelAsync(bindingContext);
- if (!result.IsModelSet)
+ await base.BindModelAsync(bindingContext);
+ if (bindingContext.Result == null || !bindingContext.Result.Value.IsModelSet)
{
// No match for the prefix at all.
- return result;
+ return;
}
+ var result = bindingContext.Result.Value;
+
Debug.Assert(result.Model != null);
var model = (IDictionary)result.Model;
if (model.Count != 0)
{
// ICollection> approach was successful.
- return result;
+ return;
}
var enumerableValueProvider = bindingContext.ValueProvider as IEnumerableValueProvider;
@@ -49,7 +51,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// No IEnumerableValueProvider available for the fallback approach. For example the user may have
// replaced the ValueProvider with something other than a CompositeValueProvider.
- return result;
+ return;
}
// Attempt to bind dictionary from a set of prefix[key]=value entries. Get the short and long keys first.
@@ -57,18 +59,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (!keys.Any())
{
// No entries with the expected keys.
- return result;
+ return;
}
// Update the existing successful but empty ModelBindingResult.
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
var valueMetadata = metadataProvider.GetMetadataForType(typeof(TValue));
- var valueBindingContext = ModelBindingContext.CreateChildBindingContext(
- bindingContext,
- valueMetadata,
- fieldName: bindingContext.FieldName,
- modelName: bindingContext.ModelName,
- model: null);
var modelBinder = bindingContext.OperationBindingContext.ModelBinder;
@@ -79,21 +75,26 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// that culture when rendering a form.
var convertedKey = ModelBindingHelper.ConvertTo(kvp.Key, culture: null);
- valueBindingContext.ModelName = kvp.Value;
+ using (bindingContext.EnterNestedScope(
+ modelMetadata: valueMetadata,
+ fieldName: bindingContext.FieldName,
+ modelName: kvp.Value,
+ model: null))
+ {
+ await modelBinder.BindModelAsync(bindingContext);
- var valueResult = await modelBinder.BindModelAsync(valueBindingContext);
+ var valueResult = bindingContext.Result;
- // Always add an entry to the dictionary but validate only if binding was successful.
- model[convertedKey] = ModelBindingHelper.CastOrDefault(valueResult.Model);
- keyMappings.Add(kvp.Key, convertedKey);
+ // Always add an entry to the dictionary but validate only if binding was successful.
+ model[convertedKey] = ModelBindingHelper.CastOrDefault(valueResult.Value.Model);
+ keyMappings.Add(kvp.Key, convertedKey);
+ }
}
bindingContext.ValidationState.Add(model, new ValidationStateEntry()
{
Strategy = new ShortFormDictionaryValidationStrategy(keyMappings, valueMetadata),
});
-
- return result;
}
///
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs
index 0813fde64d..2feaef2527 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormCollectionModelBinder.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.Primitives;
@@ -18,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class FormCollectionModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -31,13 +32,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (bindingContext.ModelType != typeof(IFormCollection))
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext);
}
- private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
+ private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
{
object model;
var request = bindingContext.OperationBindingContext.HttpContext.Request;
@@ -52,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
}
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
- return ModelBindingResult.Success(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
}
private class EmptyFormCollection : IFormCollection
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormFileModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormFileModelBinder.cs
index 794dfb6ebd..f70074a1b2 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormFileModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/FormFileModelBinder.cs
@@ -10,6 +10,7 @@ using System.Reflection;
#endif
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Net.Http.Headers;
@@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class FormFileModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -35,13 +36,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (bindingContext.ModelType != typeof(IFormFile) &&
!typeof(IEnumerable).IsAssignableFrom(bindingContext.ModelType))
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext);
}
- private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
+ private async Task BindModelCoreAsync(ModelBindingContext bindingContext)
{
// If we're at the top level, then use the FieldName (paramter or property name).
// This handles the fact that there will be nothing in the ValueProviders for this parameter
@@ -65,12 +66,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// This binder does not support the requested type.
Debug.Fail("We shouldn't be called without a matching type.");
- return ModelBindingResult.NoResult;
+ return;
}
if (value == null)
{
- return ModelBindingResult.Failed(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return;
}
else
{
@@ -85,7 +87,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
rawValue: null,
attemptedValue: null);
- return ModelBindingResult.Success(bindingContext.ModelName, value);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, value);
+ return;
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/GenericModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/GenericModelBinder.cs
index d6776c31b3..99eb8eb1cb 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/GenericModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/GenericModelBinder.cs
@@ -8,14 +8,20 @@ using System.Diagnostics;
using System.Reflection;
#endif
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
public class GenericModelBinder : IModelBinder
{
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
@@ -23,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var binderType = ResolveBinderType(bindingContext);
if (binderType == null)
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
var binder = (IModelBinder)Activator.CreateInstance(binderType);
@@ -34,24 +40,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
!collectionBinder.CanCreateInstance(bindingContext.ModelType))
{
// Able to resolve a binder type but need a new model instance and that binder cannot create it.
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext, binder);
}
- private async Task BindModelCoreAsync(ModelBindingContext bindingContext, IModelBinder binder)
+ private async Task BindModelCoreAsync(ModelBindingContext bindingContext, IModelBinder binder)
{
Debug.Assert(binder != null);
- var result = await binder.BindModelAsync(bindingContext);
- var modelBindingResult = result != ModelBindingResult.NoResult ?
- result :
- ModelBindingResult.Failed(bindingContext.ModelName);
-
- // Were able to resolve a binder type.
- // Always tell the model binding system to skip other model binders.
- return modelBindingResult;
+ await binder.BindModelAsync(bindingContext);
+ if (bindingContext.Result == null)
+ {
+ // Always tell the model binding system to skip other model binders.
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ }
}
private static Type ResolveBinderType(ModelBindingContext context)
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/HeaderModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/HeaderModelBinder.cs
index e3b3ba3353..e32c0505a5 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/HeaderModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/HeaderModelBinder.cs
@@ -1,12 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
+using System.Diagnostics;
#if DOTNET5_4
using System.Reflection;
#endif
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
@@ -17,8 +20,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class HeaderModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
@@ -29,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// Headers are opt-in. This model either didn't specify [FromHeader] or specified something
// incompatible so let other binders run.
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
var request = bindingContext.OperationBindingContext.HttpContext.Request;
@@ -59,7 +67,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (model == null)
{
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
else
{
@@ -68,7 +77,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
request.Headers.GetCommaSeparatedValues(headerName),
request.Headers[headerName]);
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return TaskCache.CompletedTask;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
index 4d272121f5..42719e10a6 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/KeyValuePairModelBinder.cs
@@ -1,15 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
public sealed class KeyValuePairModelBinder : IModelBinder
{
- public async Task BindModelAsync(ModelBindingContext bindingContext)
+ public async Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
ModelBindingHelper.ValidateBindingContext(bindingContext,
typeof(KeyValuePair),
allowNullModel: true);
@@ -23,9 +30,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
ModelBindingHelper.CastOrDefault(keyResult.Model),
ModelBindingHelper.CastOrDefault(valueResult.Model));
- return ModelBindingResult.Success(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return;
}
- else if (!keyResult.IsModelSet && valueResult.IsModelSet)
+
+ if (!keyResult.IsModelSet && valueResult.IsModelSet)
{
bindingContext.ModelState.TryAddModelError(
keyResult.Key,
@@ -33,9 +42,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Were able to get some data for this model.
// Always tell the model binding system to skip other model binders.
- return ModelBindingResult.Failed(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return;
}
- else if (keyResult.IsModelSet && !valueResult.IsModelSet)
+
+ if (keyResult.IsModelSet && !valueResult.IsModelSet)
{
bindingContext.ModelState.TryAddModelError(
valueResult.Key,
@@ -43,46 +54,46 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Were able to get some data for this model.
// Always tell the model binding system to skip other model binders.
- return ModelBindingResult.Failed(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return;
}
- else
- {
- // If we failed to find data for a top-level model, then generate a
- // default 'empty' model and return it.
- if (bindingContext.IsTopLevelObject)
- {
- var model = new KeyValuePair();
- return ModelBindingResult.Success(bindingContext.ModelName, model);
- }
- return ModelBindingResult.NoResult;
+ // If we failed to find data for a top-level model, then generate a
+ // default 'empty' model and return it.
+ if (bindingContext.IsTopLevelObject)
+ {
+ var model = new KeyValuePair();
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
}
}
internal async Task TryBindStrongModel(
- ModelBindingContext parentBindingContext,
+ ModelBindingContext bindingContext,
string propertyName)
{
var propertyModelMetadata =
- parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
+ bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
var propertyModelName =
- ModelNames.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
- var propertyBindingContext = ModelBindingContext.CreateChildBindingContext(
- parentBindingContext,
- propertyModelMetadata,
- propertyName,
- propertyModelName,
- model: null);
+ ModelNames.CreatePropertyModelName(bindingContext.ModelName, propertyName);
- var result = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
- propertyBindingContext);
- if (result.IsModelSet)
+ using (bindingContext.EnterNestedScope(
+ modelMetadata: propertyModelMetadata,
+ fieldName: propertyName,
+ modelName: propertyModelName,
+ model: null))
{
- return result;
- }
- else
- {
- return ModelBindingResult.Failed(propertyModelName);
+
+ await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
+ bindingContext);
+ var result = bindingContext.Result;
+ if (result != null && result.Value.IsModelSet)
+ {
+ return result.Value;
+ }
+ else
+ {
+ return ModelBindingResult.Failed(propertyModelName);
+ }
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
index 8d22c36ef8..84b146f45e 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/DefaultModelMetadata.cs
@@ -2,16 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Diagnostics;
using System.Linq;
-using Microsoft.AspNetCore.Mvc.Internal;
-#if DOTNET5_4
-using System.Reflection;
-#endif
-using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBindingHelper.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBindingHelper.cs
index 6b2f64f1e5..b5d018418e 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBindingHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ModelBindingHelper.cs
@@ -528,7 +528,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
ValueProvider = valueProvider,
};
- var modelBindingContext = ModelBindingContext.CreateBindingContext(
+ var modelBindingContext = DefaultModelBindingContext.CreateBindingContext(
operationBindingContext,
modelMetadata,
bindingInfo: null,
@@ -536,15 +536,16 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
modelBindingContext.Model = model;
modelBindingContext.PropertyFilter = predicate;
- var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
- if (modelBindingResult.IsModelSet)
+ await modelBinder.BindModelAsync(modelBindingContext);
+ var modelBindingResult = modelBindingContext.Result;
+ if (modelBindingResult != null && modelBindingResult.Value.IsModelSet)
{
objectModelValidator.Validate(
operationBindingContext.ActionContext,
operationBindingContext.ValidatorProvider,
modelBindingContext.ValidationState,
- modelBindingResult.Key,
- modelBindingResult.Model);
+ modelBindingResult.Value.Key,
+ modelBindingResult.Value.Model);
return modelState.IsValid;
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
index b773a4aa6b..7bf7234cb4 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
@@ -18,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
typeof(MutableObjectModelBinder).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyAddRange));
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
@@ -28,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
ModelBindingHelper.ValidateBindingContext(bindingContext);
if (!CanBindType(bindingContext.ModelMetadata))
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
var mutableObjectBinderContext = new MutableObjectBinderContext()
@@ -39,13 +40,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (!(CanCreateModel(mutableObjectBinderContext)))
{
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
return BindModelCoreAsync(bindingContext, mutableObjectBinderContext);
}
- private async Task BindModelCoreAsync(
+ private async Task BindModelCoreAsync(
ModelBindingContext bindingContext,
MutableObjectBinderContext mutableObjectBinderContext)
{
@@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
bindingContext.Model = model;
ProcessResults(bindingContext, results);
- return ModelBindingResult.Success(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
}
///
@@ -170,17 +171,17 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
context.ModelBindingContext.ModelName,
fieldName);
- var propertyModelBindingContext = ModelBindingContext.CreateChildBindingContext(
- context.ModelBindingContext,
- propertyMetadata,
+ using (context.ModelBindingContext.EnterNestedScope(
+ modelMetadata: propertyMetadata,
fieldName: fieldName,
modelName: modelName,
- model: null);
-
- // If any property can return a true value.
- if (CanBindValue(propertyModelBindingContext))
+ model: null))
{
- return true;
+ // If any property can return a true value.
+ if (CanBindValue(context.ModelBindingContext))
+ {
+ return true;
+ }
}
}
}
@@ -298,21 +299,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var fieldName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
var modelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, fieldName);
- var propertyContext = ModelBindingContext.CreateChildBindingContext(
- bindingContext,
- propertyMetadata,
+ using (bindingContext.EnterNestedScope(
+ modelMetadata: propertyMetadata,
fieldName: fieldName,
modelName: modelName,
- model: model);
-
- var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyContext);
- if (result == ModelBindingResult.NoResult)
+ model: model))
{
- // Could not bind. Let ProcessResult() know explicitly.
- result = ModelBindingResult.Failed(propertyContext.ModelName);
- }
+ await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);
+ var result = bindingContext.Result;
+ if (result == null)
+ {
+ // Could not bind. Let ProcessResult() know explicitly.
+ result = ModelBindingResult.Failed(bindingContext.ModelName);
+ }
- results[propertyMetadata] = result;
+ results[propertyMetadata] = result.Value;
+ }
}
return results;
@@ -444,7 +446,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// exceptions as necessary.
foreach (var entry in results)
{
- if (entry.Value != ModelBindingResult.NoResult)
+ if (entry.Value != null)
{
var result = entry.Value;
var propertyMetadata = entry.Key;
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ServicesModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ServicesModelBinder.cs
index 0a38fa2b33..26020e9090 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ServicesModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/ServicesModelBinder.cs
@@ -1,7 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
+using System.Diagnostics;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
@@ -14,8 +17,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class ServicesModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
@@ -26,7 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
// Services are opt-in. This model either didn't specify [FromService] or specified something
// incompatible so let other binders run.
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
@@ -34,7 +42,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return TaskCache.CompletedTask;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
index 09b4a8c4b1..8e683d5726 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs
@@ -3,13 +3,19 @@
using System;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
public class SimpleTypeModelBinder : IModelBinder
{
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
+ if (bindingContext == null)
+ {
+ throw new ArgumentNullException(nameof(bindingContext));
+ }
+
// This method is optimized to use cached tasks when possible and avoid allocating
// using Task.FromResult. If you need to make changes of this nature, profile
// allocations afterwards and look for Task.
@@ -17,14 +23,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
if (bindingContext.ModelMetadata.IsComplexType)
{
// this type cannot be converted
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
// no entry
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
@@ -53,11 +59,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(
valueProviderResult.ToString()));
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
else
{
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
+ return TaskCache.CompletedTask;
}
}
catch (Exception exception)
@@ -69,7 +77,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Were able to find a converter for the type but conversion failed.
// Tell the model binding system to skip other model binders.
- return ModelBindingResult.FailedAsync(bindingContext.ModelName);
+ bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
+ return TaskCache.CompletedTask;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageModelBinder.cs b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageModelBinder.cs
index fea443b041..3e738b8a62 100644
--- a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageModelBinder.cs
+++ b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/HttpRequestMessage/HttpRequestMessageModelBinder.cs
@@ -3,6 +3,7 @@
using System.Net.Http;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@@ -14,16 +15,16 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
public class HttpRequestMessageModelBinder : IModelBinder
{
///
- public Task BindModelAsync(ModelBindingContext bindingContext)
+ public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(HttpRequestMessage))
{
var model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage();
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
- return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
+ bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, model);
}
- return ModelBindingResult.NoResultAsync;
+ return TaskCache.CompletedTask;
}
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/BindAttributeTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/BindAttributeTest.cs
index 07204c1905..e36ad3ed86 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/BindAttributeTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/BindAttributeTest.cs
@@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Arrange
var bind = new BindAttribute(new string[] { "UserName", "FirstName", "LastName, MiddleName, ,foo,bar " });
- var context = new ModelBindingContext();
+ var context = new DefaultModelBindingContext();
// Act
var predicate = bind.PropertyFilter;
@@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Arrange
var bind = new BindAttribute(typeof(TestProvider));
- var context = new ModelBindingContext();
+ var context = new DefaultModelBindingContext();
context.OperationBindingContext = new OperationBindingContext()
{
ActionContext = new ActionContext()
@@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
// Arrange
var bind = new BindAttribute(typeof(TestProvider));
- var context = new ModelBindingContext();
+ var context = new DefaultModelBindingContext();
context.OperationBindingContext = new OperationBindingContext()
{
ActionContext = new ActionContext()
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs
index f43ac38536..6584fe2068 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ControllerBaseTest.cs
@@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
+using Microsoft.AspNetCore.Mvc.ModelBinding.Test;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.AspNetCore.Routing;
@@ -1096,29 +1097,25 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
// Arrange
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Empty(context.ModelName);
- Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Empty(context.ModelName);
+ Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
- // Include and exclude should be null, resulting in property
- // being included.
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ // Include and exclude should be null, resulting in property
+ // being included.
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
+ });
- var controller = GetController(binder.Object, valueProvider);
+ var controller = GetController(binder, valueProvider);
var model = new MyModel();
// Act
var result = await controller.TryUpdateModelAsync(model);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1129,28 +1126,24 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
- // Include and exclude should be null, resulting in property
- // being included.
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ // Include and exclude should be null, resulting in property
+ // being included.
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
+ });
- var controller = GetController(binder.Object, valueProvider);
+ var controller = GetController(binder, valueProvider);
var model = new MyModel();
// Act
var result = await controller.TryUpdateModelAsync(model, modelName);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1160,9 +1153,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var modelName = "mymodel";
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
+ var binder = new StubModelBinder(context =>
{
Assert.Same(valueProvider, context.ValueProvider);
@@ -1170,18 +1161,16 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
// being included.
Assert.True(context.PropertyFilter(context, "Property1"));
Assert.True(context.PropertyFilter(context, "Property2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ });
- var controller = GetController(binder.Object, valueProvider: null);
+ var controller = GetController(binder, valueProvider: null);
var model = new MyModel();
// Act
var result = await controller.TryUpdateModelAsync(model, modelName, valueProvider);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1194,30 +1183,26 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase);
- var binder = new Mock();
var valueProvider = Mock.Of();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
- Assert.True(context.PropertyFilter(context, "include1"));
- Assert.True(context.PropertyFilter(context, "include2"));
+ Assert.True(context.PropertyFilter(context, "include1"));
+ Assert.True(context.PropertyFilter(context, "include2"));
- Assert.False(context.PropertyFilter(context, "exclude1"));
- Assert.False(context.PropertyFilter(context, "exclude2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ Assert.False(context.PropertyFilter(context, "exclude1"));
+ Assert.False(context.PropertyFilter(context, "exclude2"));
+ });
- var controller = GetController(binder.Object, valueProvider);
+ var controller = GetController(binder, valueProvider);
var model = new MyModel();
// Act
await controller.TryUpdateModelAsync(model, modelName, includePredicate);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1230,23 +1215,18 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
(context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) ||
string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase);
- var binder = new Mock();
var valueProvider = Mock.Of();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, context.ValueProvider);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, context.ValueProvider);
- Assert.True(context.PropertyFilter(context, "include1"));
- Assert.True(context.PropertyFilter(context, "include2"));
+ Assert.True(context.PropertyFilter(context, "include1"));
+ Assert.True(context.PropertyFilter(context, "include2"));
- Assert.False(context.PropertyFilter(context, "exclude1"));
- Assert.False(context.PropertyFilter(context, "exclude2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
-
- var controller = GetController(binder.Object, valueProvider: null);
+ Assert.False(context.PropertyFilter(context, "exclude1"));
+ Assert.False(context.PropertyFilter(context, "exclude2"));
+ });
+ var controller = GetController(binder, valueProvider: null);
var model = new MyModel();
@@ -1254,7 +1234,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
await controller.TryUpdateModelAsync(model, modelName, valueProvider, includePredicate);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Theory]
@@ -1268,32 +1248,28 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
.Setup(v => v.ContainsPrefix(prefix))
.Returns(true);
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(
valueProvider.Object,
Assert.IsType(context.ValueProvider)[0]);
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
- Assert.False(context.PropertyFilter(context, "exclude1"));
- Assert.False(context.PropertyFilter(context, "exclude2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ Assert.False(context.PropertyFilter(context, "exclude1"));
+ Assert.False(context.PropertyFilter(context, "exclude2"));
+ });
- var controller = GetController(binder.Object, valueProvider.Object);
+ var controller = GetController(binder, valueProvider.Object);
var model = new MyModel();
// Act
await controller.TryUpdateModelAsync(model, prefix, m => m.Property1, m => m.Property2);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Theory]
@@ -1308,29 +1284,25 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
.Setup(v => v.ContainsPrefix(prefix))
.Returns(true);
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider.Object, context.ValueProvider);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider.Object, context.ValueProvider);
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
- Assert.False(context.PropertyFilter(context, "exclude1"));
- Assert.False(context.PropertyFilter(context, "exclude2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ Assert.False(context.PropertyFilter(context, "exclude1"));
+ Assert.False(context.PropertyFilter(context, "exclude2"));
+ });
- var controller = GetController(binder.Object, valueProvider: null);
+ var controller = GetController(binder, valueProvider: null);
var model = new MyModel();
// Act
await controller.TryUpdateModelAsync(model, prefix, valueProvider.Object, m => m.Property1, m => m.Property2);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1345,22 +1317,18 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, context.ValueProvider);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, context.ValueProvider);
- Assert.True(context.PropertyFilter(context, "include1"));
- Assert.True(context.PropertyFilter(context, "include2"));
+ Assert.True(context.PropertyFilter(context, "include1"));
+ Assert.True(context.PropertyFilter(context, "include2"));
- Assert.False(context.PropertyFilter(context, "exclude1"));
- Assert.False(context.PropertyFilter(context, "exclude2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ Assert.False(context.PropertyFilter(context, "exclude1"));
+ Assert.False(context.PropertyFilter(context, "exclude2"));
+ });
- var controller = GetController(binder.Object, valueProvider: null);
+ var controller = GetController(binder, valueProvider: null);
var model = new MyModel();
@@ -1368,7 +1336,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
await controller.TryUpdateModelAsync(model, model.GetType(), modelName, valueProvider, includePredicate);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1379,28 +1347,24 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
- // Include and exclude should be null, resulting in property
- // being included.
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ // Include and exclude should be null, resulting in property
+ // being included.
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
+ });
- var controller = GetController(binder.Object, valueProvider);
+ var controller = GetController(binder, valueProvider);
var model = new MyModel();
// Act
var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1411,28 +1375,24 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var valueProvider = Mock.Of();
- var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Callback((ModelBindingContext context) =>
- {
- Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
+ var binder = new StubModelBinder(context =>
+ {
+ Assert.Same(valueProvider, Assert.IsType(context.ValueProvider)[0]);
- // Include and exclude should be null, resulting in property
- // being included.
- Assert.True(context.PropertyFilter(context, "Property1"));
- Assert.True(context.PropertyFilter(context, "Property2"));
- })
- .Returns(ModelBindingResult.NoResultAsync)
- .Verifiable();
+ // Include and exclude should be null, resulting in property
+ // being included.
+ Assert.True(context.PropertyFilter(context, "Property1"));
+ Assert.True(context.PropertyFilter(context, "Property2"));
+ });
- var controller = GetController(binder.Object, valueProvider);
+ var controller = GetController(binder, valueProvider);
MyModel model = new MyDerivedModel();
// Act
var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName);
// Assert
- binder.Verify();
+ Assert.NotEqual(0, binder.BindModelCount);
}
[Fact]
@@ -1515,8 +1475,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
public void TryValidateModelWithValidModel_ReturnsTrue()
{
// Arrange
- var binder = new Mock();
- var controller = GetController(binder.Object, valueProvider: null);
+ var binder = new StubModelBinder();
+ var controller = GetController(binder, valueProvider: null);
controller.ControllerContext.ValidatorProviders = new List()
{
Mock.Of(),
@@ -1552,8 +1512,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
provider.Setup(v => v.GetValidators(It.IsAny()))
.Callback(c => c.Results.Add(validator1));
- var binder = new Mock();
- var controller = GetController(binder.Object, valueProvider: null);
+ var binder = new StubModelBinder();
+ var controller = GetController(binder, valueProvider: null);
controller.ControllerContext.ValidatorProviders = new List()
{
provider.Object,
@@ -1589,8 +1549,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
provider.Setup(v => v.GetValidators(It.IsAny()))
.Callback(c => c.Results.Add(validator1));
- var binder = new Mock();
- var controller = GetController(binder.Object, valueProvider: null);
+ var binder = new StubModelBinder();
+ var controller = GetController(binder, valueProvider: null);
controller.ControllerContext.ValidatorProviders = new List()
{
provider.Object,
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs
index 52e4a19bcd..4defb687e0 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionInvokerTest.cs
@@ -2090,8 +2090,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
};
var binder = new Mock();
- binder.Setup(b => b.BindModelAsync(It.IsAny()))
- .Returns(ModelBindingResult.NoResultAsync);
+ binder.Setup(b => b.BindModelAsync(It.IsAny()))
+ .Returns(TaskCache.CompletedTask);
var context = new Mock();
context.SetupGet(c => c.Items)
.Returns(new Dictionary