Reverting #1837 and add non-generic overloads for TryUpdateModel
This commit is contained in:
parent
1cc9be5df7
commit
f6e701b2b2
|
|
@ -1104,6 +1104,82 @@ namespace Microsoft.AspNet.Mvc
|
||||||
predicate);
|
predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using values from the controller's current
|
||||||
|
/// <see cref="IValueProvider"/> and a <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the current <see cref="IValueProvider"/>
|
||||||
|
/// </param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
||||||
|
[NonAction]
|
||||||
|
public virtual async Task<bool> TryUpdateModelAsync([NotNull] object model,
|
||||||
|
[NotNull] Type modelType,
|
||||||
|
string prefix)
|
||||||
|
{
|
||||||
|
if (BindingContext == null)
|
||||||
|
{
|
||||||
|
var message = Resources.FormatPropertyOfTypeCannotBeNull(
|
||||||
|
nameof(BindingContext),
|
||||||
|
typeof(Controller).FullName);
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
prefix,
|
||||||
|
ActionContext.HttpContext,
|
||||||
|
ModelState,
|
||||||
|
MetadataProvider,
|
||||||
|
BindingContext.ModelBinder,
|
||||||
|
BindingContext.ValueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
BindingContext.ValidatorProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the <paramref name="valueProvider"/> and a
|
||||||
|
/// <paramref name="prefix"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>
|
||||||
|
/// </param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="predicate">A predicate which can be used to filter properties at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
||||||
|
[NonAction]
|
||||||
|
public async Task<bool> TryUpdateModelAsync(
|
||||||
|
[NotNull] object model,
|
||||||
|
[NotNull] Type modelType,
|
||||||
|
string prefix,
|
||||||
|
[NotNull] IValueProvider valueProvider,
|
||||||
|
[NotNull] Func<ModelBindingContext, string, bool> predicate)
|
||||||
|
{
|
||||||
|
if (BindingContext == null)
|
||||||
|
{
|
||||||
|
var message = Resources.FormatPropertyOfTypeCannotBeNull(
|
||||||
|
nameof(BindingContext),
|
||||||
|
typeof(Controller).FullName);
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
prefix,
|
||||||
|
ActionContext.HttpContext,
|
||||||
|
ModelState,
|
||||||
|
MetadataProvider,
|
||||||
|
BindingContext.ModelBinder,
|
||||||
|
valueProvider,
|
||||||
|
ObjectValidator,
|
||||||
|
BindingContext.ValidatorProvider,
|
||||||
|
predicate);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates the specified <paramref name="model"/> instance.
|
/// Validates the specified <paramref name="model"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -128,14 +128,14 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// <param name="metadataProvider">The provider used for reading metadata for the model type.</param>
|
/// <param name="metadataProvider">The provider used for reading metadata for the model type.</param>
|
||||||
/// <param name="modelBinder">The <see cref="IModelBinder"/> used for binding.</param>
|
/// <param name="modelBinder">The <see cref="IModelBinder"/> used for binding.</param>
|
||||||
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
/// /// <param name="objectModelValidator">The <see cref="IObjectModelValidator"/> used for validating the
|
/// <param name="objectModelValidator">The <see cref="IObjectModelValidator"/> used for validating the
|
||||||
/// bound values.</param>
|
/// bound values.</param>
|
||||||
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/> used for executing validation
|
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/> used for executing validation
|
||||||
/// on the model instance.</param>
|
/// on the model instance.</param>
|
||||||
/// <param name="predicate">A predicate which can be used to
|
/// <param name="predicate">A predicate which can be used to
|
||||||
/// filter properties(for inclusion/exclusion) at runtime.</param>
|
/// filter properties(for inclusion/exclusion) at runtime.</param>
|
||||||
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
||||||
public static async Task<bool> TryUpdateModelAsync<TModel>(
|
public static Task<bool> TryUpdateModelAsync<TModel>(
|
||||||
[NotNull] TModel model,
|
[NotNull] TModel model,
|
||||||
[NotNull] string prefix,
|
[NotNull] string prefix,
|
||||||
[NotNull] HttpContext httpContext,
|
[NotNull] HttpContext httpContext,
|
||||||
|
|
@ -148,9 +148,113 @@ namespace Microsoft.AspNet.Mvc
|
||||||
[NotNull] Func<ModelBindingContext, string, bool> predicate)
|
[NotNull] Func<ModelBindingContext, string, bool> predicate)
|
||||||
where TModel : class
|
where TModel : class
|
||||||
{
|
{
|
||||||
|
return TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
typeof(TModel),
|
||||||
|
prefix,
|
||||||
|
httpContext,
|
||||||
|
modelState,
|
||||||
|
metadataProvider,
|
||||||
|
modelBinder,
|
||||||
|
valueProvider,
|
||||||
|
objectModelValidator,
|
||||||
|
validatorProvider,
|
||||||
|
predicate: predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the specified <paramref name="modelBinder"/>
|
||||||
|
/// and the specified <paramref name="valueProvider"/> and executes validation using the specified
|
||||||
|
/// <paramref name="validatorProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update and validate.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update and validate.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="httpContext">The <see cref="HttpContext"/> for the current executing request.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/> used for maintaining state and
|
||||||
|
/// results of model-binding validation.</param>
|
||||||
|
/// <param name="metadataProvider">The provider used for reading metadata for the model type.</param>
|
||||||
|
/// <param name="modelBinder">The <see cref="IModelBinder"/> used for binding.</param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="objectModelValidator">The <see cref="IObjectModelValidator"/> used for validating the
|
||||||
|
/// bound values.</param>
|
||||||
|
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/> used for executing validation
|
||||||
|
/// on the model instance.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
||||||
|
public static Task<bool> TryUpdateModelAsync(
|
||||||
|
[NotNull] object model,
|
||||||
|
[NotNull] Type modelType,
|
||||||
|
[NotNull] string prefix,
|
||||||
|
[NotNull] HttpContext httpContext,
|
||||||
|
[NotNull] ModelStateDictionary modelState,
|
||||||
|
[NotNull] IModelMetadataProvider metadataProvider,
|
||||||
|
[NotNull] IModelBinder modelBinder,
|
||||||
|
[NotNull] IValueProvider valueProvider,
|
||||||
|
[NotNull] IObjectModelValidator objectModelValidator,
|
||||||
|
[NotNull] IModelValidatorProvider validatorProvider)
|
||||||
|
{
|
||||||
|
// Includes everything by default.
|
||||||
|
return TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType,
|
||||||
|
prefix,
|
||||||
|
httpContext,
|
||||||
|
modelState,
|
||||||
|
metadataProvider,
|
||||||
|
modelBinder,
|
||||||
|
valueProvider,
|
||||||
|
objectModelValidator,
|
||||||
|
validatorProvider,
|
||||||
|
predicate: (context, propertyName) => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the specified <paramref name="model"/> instance using the specified <paramref name="modelBinder"/>
|
||||||
|
/// and the specified <paramref name="valueProvider"/> and executes validation using the specified
|
||||||
|
/// <paramref name="validatorProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The model instance to update and validate.</param>
|
||||||
|
/// <param name="modelType">The type of model instance to update and validate.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in the <paramref name="valueProvider"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="httpContext">The <see cref="HttpContext"/> for the current executing request.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/> used for maintaining state and
|
||||||
|
/// results of model-binding validation.</param>
|
||||||
|
/// <param name="metadataProvider">The provider used for reading metadata for the model type.</param>
|
||||||
|
/// <param name="modelBinder">The <see cref="IModelBinder"/> used for binding.</param>
|
||||||
|
/// <param name="valueProvider">The <see cref="IValueProvider"/> used for looking up values.</param>
|
||||||
|
/// <param name="objectModelValidator">The <see cref="IObjectModelValidator"/> used for validating the
|
||||||
|
/// bound values.</param>
|
||||||
|
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/> used for executing validation
|
||||||
|
/// on the model instance.</param>
|
||||||
|
/// <param name="predicate">A predicate which can be used to
|
||||||
|
/// filter properties(for inclusion/exclusion) at runtime.</param>
|
||||||
|
/// <returns>A <see cref="Task"/> that on completion returns <c>true</c> if the update is successful</returns>
|
||||||
|
public static async Task<bool> TryUpdateModelAsync(
|
||||||
|
[NotNull] object model,
|
||||||
|
[NotNull] Type modelType,
|
||||||
|
[NotNull] string prefix,
|
||||||
|
[NotNull] HttpContext httpContext,
|
||||||
|
[NotNull] ModelStateDictionary modelState,
|
||||||
|
[NotNull] IModelMetadataProvider metadataProvider,
|
||||||
|
[NotNull] IModelBinder modelBinder,
|
||||||
|
[NotNull] IValueProvider valueProvider,
|
||||||
|
[NotNull] IObjectModelValidator objectModelValidator,
|
||||||
|
[NotNull] IModelValidatorProvider validatorProvider,
|
||||||
|
[NotNull] Func<ModelBindingContext, string, bool> predicate)
|
||||||
|
{
|
||||||
|
if (!modelType.IsAssignableFrom(model.GetType()))
|
||||||
|
{
|
||||||
|
var message = Resources.FormatModelType_WrongType(
|
||||||
|
model.GetType().FullName,
|
||||||
|
modelType.FullName);
|
||||||
|
throw new ArgumentException(message, nameof(modelType));
|
||||||
|
}
|
||||||
|
|
||||||
var modelMetadata = metadataProvider.GetMetadataForType(
|
var modelMetadata = metadataProvider.GetMetadataForType(
|
||||||
modelAccessor: () => model,
|
modelAccessor: () => model,
|
||||||
modelType: model.GetType());
|
modelType: modelType);
|
||||||
|
|
||||||
var operationBindingContext = new OperationBindingContext
|
var operationBindingContext = new OperationBindingContext
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1706,6 +1706,14 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("CacheProfileNotFound"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("CacheProfileNotFound"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model type '{0}' does not match the '{1}' type parameter.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatModelType_WrongType(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("ModelType_WrongType"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -445,4 +445,7 @@
|
||||||
<data name="CacheProfileNotFound" xml:space="preserve">
|
<data name="CacheProfileNotFound" xml:space="preserve">
|
||||||
<value>The '{0}' cache profile is not defined.</value>
|
<value>The '{0}' cache profile is not defined.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ModelType_WrongType" xml:space="preserve">
|
||||||
|
<value>The model's runtime type '{0}' is not assignable to the type '{1}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1140,6 +1140,110 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
binder.Verify();
|
binder.Verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_PredicateWithValueProviderOverload_UsesPassedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var modelName = "mymodel";
|
||||||
|
|
||||||
|
Func<ModelBindingContext, string, bool> includePredicate =
|
||||||
|
(context, propertyName) => string.Equals(propertyName, "include1", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(propertyName, "include2", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
var valueProvider = Mock.Of<IValueProvider>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback((ModelBindingContext context) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(modelName, context.ModelName);
|
||||||
|
Assert.Same(valueProvider, context.ValueProvider);
|
||||||
|
|
||||||
|
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(Task.FromResult<ModelBindingResult>(null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var controller = GetController(binder.Object, provider: null);
|
||||||
|
|
||||||
|
var model = new MyModel();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await controller.TryUpdateModelAsync(model, model.GetType(), modelName, valueProvider, includePredicate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
binder.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_ModelTypeOverload_UsesPassedArguments()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var modelName = "mymodel";
|
||||||
|
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
var valueProvider = Mock.Of<IValueProvider>();
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback((ModelBindingContext context) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(modelName, context.ModelName);
|
||||||
|
Assert.Same(valueProvider, context.ValueProvider);
|
||||||
|
|
||||||
|
// Include and exclude should be null, resulting in property
|
||||||
|
// being included.
|
||||||
|
Assert.True(context.PropertyFilter(context, "Property1"));
|
||||||
|
Assert.True(context.PropertyFilter(context, "Property2"));
|
||||||
|
})
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var controller = GetController(binder.Object, valueProvider);
|
||||||
|
var model = new MyModel();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
binder.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_BindToBaseDeclaredType_ModelTypeOverload()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var modelName = "mymodel";
|
||||||
|
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
var valueProvider = Mock.Of<IValueProvider>();
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback((ModelBindingContext context) =>
|
||||||
|
{
|
||||||
|
Assert.Equal(modelName, context.ModelName);
|
||||||
|
Assert.Same(valueProvider, context.ValueProvider);
|
||||||
|
|
||||||
|
// Include and exclude should be null, resulting in property
|
||||||
|
// being included.
|
||||||
|
Assert.True(context.PropertyFilter(context, "Property1"));
|
||||||
|
Assert.True(context.PropertyFilter(context, "Property2"));
|
||||||
|
})
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var controller = GetController(binder.Object, valueProvider);
|
||||||
|
MyModel model = new MyDerivedModel();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await controller.TryUpdateModelAsync(model, model.GetType(), modelName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
binder.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1366,6 +1470,11 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
public string Property2 { get; set; }
|
public string Property2 { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MyDerivedModel : MyModel
|
||||||
|
{
|
||||||
|
public string Property3 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private class User
|
private class User
|
||||||
{
|
{
|
||||||
public User(int id)
|
public User(int id)
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,222 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
ex.Message);
|
ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_PredicateOverload_ReturnsFalse_IfBinderReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new Mock<IModelMetadataProvider>();
|
||||||
|
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
|
||||||
|
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null));
|
||||||
|
var model = new MyModel();
|
||||||
|
Func<ModelBindingContext, string, bool> includePredicate =
|
||||||
|
(context, propertyName) => true;
|
||||||
|
// Act
|
||||||
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
model.GetType(),
|
||||||
|
prefix: null,
|
||||||
|
httpContext: Mock.Of<HttpContext>(),
|
||||||
|
modelState: new ModelStateDictionary(),
|
||||||
|
metadataProvider: metadataProvider.Object,
|
||||||
|
modelBinder: GetCompositeBinder(binder.Object),
|
||||||
|
valueProvider: Mock.Of<IValueProvider>(),
|
||||||
|
objectModelValidator: Mock.Of<IObjectModelValidator>(),
|
||||||
|
validatorProvider: Mock.Of<IModelValidatorProvider>(),
|
||||||
|
predicate: includePredicate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(model.MyProperty);
|
||||||
|
Assert.Null(model.IncludedProperty);
|
||||||
|
Assert.Null(model.ExcludedProperty);
|
||||||
|
metadataProvider.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_PredicateOverload_ReturnsTrue_ModelBindsAndValidatesSuccessfully()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var binders = new IModelBinder[]
|
||||||
|
{
|
||||||
|
new TypeConverterModelBinder(),
|
||||||
|
new ComplexModelDtoModelBinder(),
|
||||||
|
new MutableObjectModelBinder()
|
||||||
|
};
|
||||||
|
|
||||||
|
var validator = new DataAnnotationsModelValidatorProvider();
|
||||||
|
var model = new MyModel
|
||||||
|
{
|
||||||
|
MyProperty = "Old-Value",
|
||||||
|
IncludedProperty = "Old-IncludedPropertyValue",
|
||||||
|
ExcludedProperty = "Old-ExcludedPropertyValue"
|
||||||
|
};
|
||||||
|
|
||||||
|
var modelStateDictionary = new ModelStateDictionary();
|
||||||
|
var values = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "", null },
|
||||||
|
{ "MyProperty", "MyPropertyValue" },
|
||||||
|
{ "IncludedProperty", "IncludedPropertyValue" },
|
||||||
|
{ "ExcludedProperty", "ExcludedPropertyValue" }
|
||||||
|
};
|
||||||
|
|
||||||
|
Func<ModelBindingContext, string, bool> includePredicate =
|
||||||
|
(context, propertyName) =>
|
||||||
|
string.Equals(propertyName, "IncludedProperty", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals(propertyName, "MyProperty", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var valueProvider = new TestValueProvider(values);
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
model.GetType(),
|
||||||
|
"",
|
||||||
|
Mock.Of<HttpContext>(),
|
||||||
|
modelStateDictionary,
|
||||||
|
metadataProvider,
|
||||||
|
GetCompositeBinder(binders),
|
||||||
|
valueProvider,
|
||||||
|
new DefaultObjectValidator(
|
||||||
|
Mock.Of<IValidationExcludeFiltersProvider>(),
|
||||||
|
metadataProvider),
|
||||||
|
validator,
|
||||||
|
includePredicate);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal("MyPropertyValue", model.MyProperty);
|
||||||
|
Assert.Equal("IncludedPropertyValue", model.IncludedProperty);
|
||||||
|
Assert.Equal("Old-ExcludedPropertyValue", model.ExcludedProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_ModelTypeOverload_ReturnsFalse_IfBinderReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new Mock<IModelMetadataProvider>();
|
||||||
|
metadataProvider.Setup(m => m.GetMetadataForType(It.IsAny<Func<object>>(), It.IsAny<Type>()))
|
||||||
|
.Returns(new ModelMetadata(metadataProvider.Object, null, null, typeof(MyModel), null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null));
|
||||||
|
var model = new MyModel();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
modelType: model.GetType(),
|
||||||
|
prefix: null,
|
||||||
|
httpContext: Mock.Of<HttpContext>(),
|
||||||
|
modelState: new ModelStateDictionary(),
|
||||||
|
metadataProvider: metadataProvider.Object,
|
||||||
|
modelBinder: GetCompositeBinder(binder.Object),
|
||||||
|
valueProvider: Mock.Of<IValueProvider>(),
|
||||||
|
objectModelValidator: Mock.Of<IObjectModelValidator>(),
|
||||||
|
validatorProvider: Mock.Of<IModelValidatorProvider>());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(model.MyProperty);
|
||||||
|
metadataProvider.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_ModelTypeOverload_ReturnsTrue_IfModelBindsAndValidatesSuccessfully()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var binders = new IModelBinder[]
|
||||||
|
{
|
||||||
|
new TypeConverterModelBinder(),
|
||||||
|
new ComplexModelDtoModelBinder(),
|
||||||
|
new MutableObjectModelBinder()
|
||||||
|
};
|
||||||
|
|
||||||
|
var validator = new DataAnnotationsModelValidatorProvider();
|
||||||
|
var model = new MyModel { MyProperty = "Old-Value" };
|
||||||
|
var modelStateDictionary = new ModelStateDictionary();
|
||||||
|
var values = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "", null },
|
||||||
|
{ "MyProperty", "MyPropertyValue" }
|
||||||
|
};
|
||||||
|
var valueProvider = new TestValueProvider(values);
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
model.GetType(),
|
||||||
|
"",
|
||||||
|
Mock.Of<HttpContext>(),
|
||||||
|
modelStateDictionary,
|
||||||
|
new DataAnnotationsModelMetadataProvider(),
|
||||||
|
GetCompositeBinder(binders),
|
||||||
|
valueProvider,
|
||||||
|
new DefaultObjectValidator(
|
||||||
|
Mock.Of<IValidationExcludeFiltersProvider>(),
|
||||||
|
metadataProvider),
|
||||||
|
validator);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal("MyPropertyValue", model.MyProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdataModel_ModelTypeDifferentFromModel_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new Mock<IModelMetadataProvider>();
|
||||||
|
metadataProvider.Setup(m => m.GetMetadataForType(null, It.IsAny<Type>()))
|
||||||
|
.Returns(new ModelMetadata(metadataProvider.Object,
|
||||||
|
containerType: null,
|
||||||
|
modelAccessor: null,
|
||||||
|
modelType: typeof(MyModel),
|
||||||
|
propertyName: null))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null));
|
||||||
|
var model = new MyModel();
|
||||||
|
Func<ModelBindingContext, string, bool> includePredicate =
|
||||||
|
(context, propertyName) => true;
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = await Assert.ThrowsAsync<ArgumentException>(
|
||||||
|
() => ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
model,
|
||||||
|
typeof(User),
|
||||||
|
null,
|
||||||
|
Mock.Of<HttpContext>(),
|
||||||
|
new ModelStateDictionary(),
|
||||||
|
metadataProvider.Object,
|
||||||
|
GetCompositeBinder(binder.Object),
|
||||||
|
Mock.Of<IValueProvider>(),
|
||||||
|
new DefaultObjectValidator(
|
||||||
|
Mock.Of<IValidationExcludeFiltersProvider>(),
|
||||||
|
metadataProvider.Object),
|
||||||
|
Mock.Of<IModelValidatorProvider>(),
|
||||||
|
includePredicate));
|
||||||
|
|
||||||
|
var expectedMessage = string.Format("The model's runtime type '{0}' is not assignable to the type '{1}'." +
|
||||||
|
Environment.NewLine +
|
||||||
|
"Parameter name: modelType",
|
||||||
|
model.GetType().FullName,
|
||||||
|
typeof(User).FullName);
|
||||||
|
Assert.Equal(expectedMessage, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
private static IModelBinder GetCompositeBinder(params IModelBinder[] binders)
|
private static IModelBinder GetCompositeBinder(params IModelBinder[] binders)
|
||||||
{
|
{
|
||||||
return new CompositeModelBinder(binders);
|
return new CompositeModelBinder(binders);
|
||||||
|
|
|
||||||
|
|
@ -398,7 +398,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("WA_Query", user.ShippingAddress.State);
|
Assert.Equal("WA_Query", user.ShippingAddress.State);
|
||||||
Assert.Equal(3, user.ShippingAddress.Street);
|
Assert.Equal(3, user.ShippingAddress.Street);
|
||||||
Assert.Equal(4, user.ShippingAddress.Zip);
|
Assert.Equal(4, user.ShippingAddress.Zip);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1573,6 +1572,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ModelBinder_FormatsDontMatch_ThrowsUserFriendlyException()
|
public async Task ModelBinder_FormatsDontMatch_ThrowsUserFriendlyException()
|
||||||
|
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var server = TestServer.Create(_services, _app);
|
var server = TestServer.Create(_services, _app);
|
||||||
|
|
@ -1644,6 +1644,29 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(expectedDictionary, dictionary);
|
Assert.Equal(expectedDictionary, dictionary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGeneric_IncludesAllProperties_CanBind()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetStringAsync("http://localhost/TryUpdateModel/" +
|
||||||
|
"GetUserAsync_ModelType_IncludeAll" +
|
||||||
|
"?id=123&RegisterationMonth=March&Key=123&UserName=SomeName");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var user = JsonConvert.DeserializeObject<User>(response);
|
||||||
|
|
||||||
|
// Should not update any not explicitly mentioned properties.
|
||||||
|
Assert.Equal("SomeName", user.UserName);
|
||||||
|
Assert.Equal(123, user.Key);
|
||||||
|
|
||||||
|
// Should Update all included properties.
|
||||||
|
Assert.Equal("March", user.RegisterationMonth);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FormCollectionModelBinder_CanBind_FormValues()
|
public async Task FormCollectionModelBinder_CanBind_FormValues()
|
||||||
{
|
{
|
||||||
|
|
@ -1731,5 +1754,28 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var fileContent = await response.Content.ReadAsStringAsync();
|
var fileContent = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(expectedContent, fileContent);
|
Assert.Equal(expectedContent, fileContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryUpdateModelNonGenericIncludesAllProperties_ByDefault()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetStringAsync("http://localhost/TryUpdateModel/" +
|
||||||
|
"GetUserAsync_ModelType_IncludeAllByDefault" +
|
||||||
|
"?id=123&RegisterationMonth=March&Key=123&UserName=SomeName");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var user = JsonConvert.DeserializeObject<User>(response);
|
||||||
|
|
||||||
|
// Should not update any not explicitly mentioned properties.
|
||||||
|
Assert.Equal("SomeName", user.UserName);
|
||||||
|
Assert.Equal(123, user.Key);
|
||||||
|
|
||||||
|
// Should Update all included properties.
|
||||||
|
Assert.Equal("March", user.RegisterationMonth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,9 +2,13 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Http.Core.Collections;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
|
||||||
namespace ModelBindingWebSite.Controllers
|
namespace ModelBindingWebSite.Controllers
|
||||||
{
|
{
|
||||||
|
|
@ -100,12 +104,58 @@ namespace ModelBindingWebSite.Controllers
|
||||||
|
|
||||||
public async Task<Employee> GetEmployeeAsync_BindToBaseDeclaredType()
|
public async Task<Employee> GetEmployeeAsync_BindToBaseDeclaredType()
|
||||||
{
|
{
|
||||||
var employee = new Employee();
|
var backingStore = new ReadableStringCollection(
|
||||||
await TryUpdateModelAsync<Person>(
|
new Dictionary<string, string[]>
|
||||||
employee,
|
{
|
||||||
prefix: string.Empty);
|
{ "Parent.Name", new[] { "fatherName"} },
|
||||||
|
{ "Parent.Parent.Name", new[] {"grandFatherName" } },
|
||||||
|
{ "Department", new[] {"Sales" } }
|
||||||
|
});
|
||||||
|
|
||||||
return employee;
|
Person employee = new Employee();
|
||||||
|
await TryUpdateModelAsync(
|
||||||
|
employee,
|
||||||
|
employee.GetType(),
|
||||||
|
prefix: string.Empty,
|
||||||
|
valueProvider: new ReadableStringCollectionValueProvider(
|
||||||
|
BindingSource.Query,
|
||||||
|
backingStore,
|
||||||
|
CultureInfo.CurrentCulture),
|
||||||
|
predicate: (content, propertyName) => true);
|
||||||
|
|
||||||
|
return (Employee)employee;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> GetUserAsync_ModelType_IncludeAll(int id)
|
||||||
|
{
|
||||||
|
var backingStore = new ReadableStringCollection(
|
||||||
|
new Dictionary<string, string[]>
|
||||||
|
{
|
||||||
|
{ "Key", new[] { "123"} },
|
||||||
|
{ "RegisterationMonth", new[] {"March" } },
|
||||||
|
{ "UserName", new[] {"SomeName" } }
|
||||||
|
});
|
||||||
|
|
||||||
|
var user = GetUser(id);
|
||||||
|
|
||||||
|
await TryUpdateModelAsync(user,
|
||||||
|
typeof(User),
|
||||||
|
prefix: string.Empty,
|
||||||
|
valueProvider: new ReadableStringCollectionValueProvider(
|
||||||
|
BindingSource.Query,
|
||||||
|
backingStore,
|
||||||
|
CultureInfo.CurrentCulture),
|
||||||
|
predicate: (content, propertyName) => true);
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> GetUserAsync_ModelType_IncludeAllByDefault(int id)
|
||||||
|
{
|
||||||
|
var user = GetUser(id);
|
||||||
|
|
||||||
|
await TryUpdateModelAsync(user, user.GetType(), prefix: string.Empty);
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private User GetUser(int id)
|
private User GetUser(int id)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue