Merge pull request #7671 from aspnet/release/2.1

Merge into `dev`: Include parameter type's attributes in ModelMetadata
This commit is contained in:
Doug Bunting 2018-04-17 13:00:21 -07:00 committed by GitHub
commit cd6d130a8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 446 additions and 225 deletions

View File

@ -5,8 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
namespace Microsoft.AspNetCore.Mvc.ModelBinding namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
@ -56,7 +54,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// If this instance represents a parameter, the set of attributes for that parameter. /// If this instance represents a parameter, the set of attributes for that parameter.
/// Otherwise, <c>null</c>. /// Otherwise, <c>null</c>.
/// </param> /// </param>
public ModelAttributes(IEnumerable<object> typeAttributes, IEnumerable<object> propertyAttributes, IEnumerable<object> parameterAttributes) internal ModelAttributes(
IEnumerable<object> typeAttributes,
IEnumerable<object> propertyAttributes,
IEnumerable<object> parameterAttributes)
{ {
if (propertyAttributes != null) if (propertyAttributes != null)
{ {
@ -73,7 +74,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
else if (parameterAttributes != null) else if (parameterAttributes != null)
{ {
// Represents a parameter // Represents a parameter
Attributes = ParameterAttributes = parameterAttributes.ToArray(); if (typeAttributes == null)
{
throw new ArgumentNullException(nameof(typeAttributes));
}
ParameterAttributes = parameterAttributes.ToArray();
TypeAttributes = typeAttributes.ToArray();
Attributes = ParameterAttributes.Concat(TypeAttributes).ToArray();
} }
else if (typeAttributes != null) else if (typeAttributes != null)
{ {
@ -89,7 +97,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <summary> /// <summary>
/// Gets the set of all attributes. If this instance represents the attributes for a property, the attributes /// Gets the set of all attributes. If this instance represents the attributes for a property, the attributes
/// on the property definition are before those on the property's <see cref="Type"/>. /// on the property definition are before those on the property's <see cref="Type"/>. If this instance
/// represents the attributes for a parameter, the attributes on the parameter definition are before those on
/// the parameter's <see cref="Type"/>.
/// </summary> /// </summary>
public IReadOnlyList<object> Attributes { get; } public IReadOnlyList<object> Attributes { get; }
@ -106,10 +116,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public IReadOnlyList<object> ParameterAttributes { get; } public IReadOnlyList<object> ParameterAttributes { get; }
/// <summary> /// <summary>
/// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property, /// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property, then
/// then <see cref="TypeAttributes"/> contains attributes retrieved from /// <see cref="TypeAttributes"/> contains attributes retrieved from <see cref="PropertyInfo.PropertyType"/>.
/// <see cref="PropertyInfo.PropertyType"/>. If this instance represents a parameter, then /// If this instance represents a parameter, then contains attributes retrieved from
/// the value is <c>null</c>. /// <see cref="ParameterInfo.ParameterType"/>.
/// </summary> /// </summary>
public IReadOnlyList<object> TypeAttributes { get; } public IReadOnlyList<object> TypeAttributes { get; }
@ -120,7 +130,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// </param> /// </param>
/// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved. /// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved.
/// </param> /// </param>
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns> /// <returns>
/// A <see cref="ModelAttributes"/> instance with the attributes of the property and its <see cref="Type"/>.
/// </returns>
public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property) public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property)
{ {
if (type == null) if (type == null)
@ -146,7 +158,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
} }
} }
return new ModelAttributes(typeAttributes, propertyAttributes, null); return new ModelAttributes(typeAttributes, propertyAttributes, parameterAttributes: null);
} }
/// <summary> /// <summary>
@ -170,18 +182,27 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
attributes = attributes.Concat(metadataType.GetTypeInfo().GetCustomAttributes()); attributes = attributes.Concat(metadataType.GetTypeInfo().GetCustomAttributes());
} }
return new ModelAttributes(attributes, null, null); return new ModelAttributes(attributes, propertyAttributes: null, parameterAttributes: null);
} }
/// <summary> /// <summary>
/// Gets the attributes for the given <paramref name="parameterInfo"/>. /// Gets the attributes for the given <paramref name="parameterInfo"/>.
/// </summary> /// </summary>
/// <param name="parameterInfo">The <see cref="ParameterInfo"/> for which attributes need to be resolved. /// <param name="parameterInfo">
/// The <see cref="ParameterInfo"/> for which attributes need to be resolved.
/// </param> /// </param>
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="ParameterInfo"/>.</returns> /// <returns>
/// A <see cref="ModelAttributes"/> instance with the attributes of the parameter and its <see cref="Type"/>.
/// </returns>
public static ModelAttributes GetAttributesForParameter(ParameterInfo parameterInfo) public static ModelAttributes GetAttributesForParameter(ParameterInfo parameterInfo)
{ {
return new ModelAttributes(null, null, parameterInfo.GetCustomAttributes()); // Prior versions called IModelMetadataProvider.GetMetadataForType(...) and therefore
// GetAttributesForType(...) for parameters. Maintain that set of attributes (including those from an
// ModelMetadataTypeAttribute reference) for back-compatibility.
var typeAttributes = GetAttributesForType(parameterInfo.ParameterType).TypeAttributes;
var parameterAttributes = parameterInfo.GetCustomAttributes();
return new ModelAttributes(typeAttributes, propertyAttributes: null, parameterAttributes);
} }
private static Type GetMetadataType(Type type) private static Type GetMetadataType(Type type)

View File

@ -176,7 +176,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
// Arrange // Arrange
var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), }; var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), };
var modelAttributes = new ModelAttributes(Enumerable.Empty<object>(), null, null);
var propertyFilterProvider = Mock.Of<IPropertyFilterProvider>(); var propertyFilterProvider = Mock.Of<IPropertyFilterProvider>();
var modelType = typeof(Guid); var modelType = typeof(Guid);
var provider = new TestModelMetadataProvider(); var provider = new TestModelMetadataProvider();

View File

@ -279,7 +279,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();
@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();
@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();
@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();
@ -371,7 +371,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();
@ -395,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var context = new BindingMetadataProviderContext( var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo),
new ModelAttributes(null, null, parameterAttributes)); new ModelAttributes(Array.Empty<object>(), null, parameterAttributes));
var provider = new DefaultBindingMetadataProvider(); var provider = new DefaultBindingMetadataProvider();

View File

@ -17,12 +17,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test
{ {
private static readonly Dictionary<string, StringValues> _backingStore = new Dictionary<string, StringValues> private static readonly Dictionary<string, StringValues> _backingStore = new Dictionary<string, StringValues>
{ {
// Reviewers: A fair number of cases in the following dataset seem impossible for jQuery.ajax() to send
// given how $.ajax() and $.param() are defined i.e. $.param() won't add a bare name after ']'. Also, the
// names we generate for <input/> elements are always valid. Should we remove these weird / invalid cases
// e.g. normalizing "property[]Value"? (That normalizes to "propertyValue".) See also
// https://github.com/jquery/jquery/blob/1ea092a54b00aa4d902f4e22ada3854d195d4a18/src/serialize.js#L13-L92
// The alternative is to handle "[]name" and "[index]name" cases while normalizing keys.
{ "[]", new[] { "found" } }, { "[]", new[] { "found" } },
{ "[]property1", new[] { "found" } }, { "[]property1", new[] { "found" } },
{ "property2[]", new[] { "found" } }, { "property2[]", new[] { "found" } },

View File

@ -212,7 +212,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
// Assert // Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata); var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
Assert.Empty(defaultMetadata.Attributes.Attributes);
// Not exactly "no attributes" due to SerializableAttribute on object.
Assert.IsType<SerializableAttribute>(Assert.Single(defaultMetadata.Attributes.Attributes));
} }
[Fact] [Fact]
@ -229,11 +231,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
// Assert // Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata); var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
Assert.Equal(2, defaultMetadata.Attributes.Attributes.Count); Assert.Collection(
var attribute1 = Assert.IsType<ModelAttribute>(defaultMetadata.Attributes.Attributes[0]); // Take(2) to ignore SerializableAttribute on object.
Assert.Equal("ParamAttrib1", attribute1.Value); defaultMetadata.Attributes.Attributes.Take(2),
var attribute2 = Assert.IsType<ModelAttribute>(defaultMetadata.Attributes.Attributes[1]); attribute =>
Assert.Equal("ParamAttrib2", attribute2.Value); {
var modelAttribute = Assert.IsType<ModelAttribute>(attribute);
Assert.Equal("ParamAttrib1", modelAttribute.Value);
},
attribute =>
{
var modelAttribute = Assert.IsType<ModelAttribute>(attribute);
Assert.Equal("ParamAttrib2", modelAttribute.Value);
});
} }
[Fact] [Fact]

View File

@ -5,6 +5,7 @@ using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.ModelBinding namespace Microsoft.AspNetCore.Mvc.ModelBinding
@ -188,10 +189,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
.GetParameters()[0]); .GetParameters()[0]);
// Assert // Assert
Assert.Empty(attributes.Attributes); // Not exactly "no attributes" due to SerializableAttribute on object.
Assert.IsType<SerializableAttribute>(Assert.Single(attributes.Attributes));
Assert.Empty(attributes.ParameterAttributes); Assert.Empty(attributes.ParameterAttributes);
Assert.Null(attributes.TypeAttributes);
Assert.Null(attributes.PropertyAttributes); Assert.Null(attributes.PropertyAttributes);
Assert.Equal(attributes.Attributes, attributes.TypeAttributes);
} }
[Fact] [Fact]
@ -204,12 +206,40 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
.GetParameters()[1]); .GetParameters()[1]);
// Assert // Assert
Assert.IsType<RequiredAttribute>(attributes.Attributes[0]); Assert.Collection(
Assert.IsType<RangeAttribute>(attributes.Attributes[1]); // Take(2) to ignore ComVisibleAttribute, SerializableAttribute, ... on int.
Assert.IsType<RequiredAttribute>(attributes.ParameterAttributes[0]); attributes.Attributes.Take(2),
Assert.IsType<RangeAttribute>(attributes.ParameterAttributes[1]); attribute => Assert.IsType<RequiredAttribute>(attribute),
Assert.Null(attributes.TypeAttributes); attribute => Assert.IsType<RangeAttribute>(attribute));
Assert.Collection(
attributes.ParameterAttributes,
attribute => Assert.IsType<RequiredAttribute>(attribute),
attribute => Assert.IsType<RangeAttribute>(attribute));
Assert.Null(attributes.PropertyAttributes); Assert.Null(attributes.PropertyAttributes);
Assert.Collection(
// Take(1) because the attribute or attributes after SerializableAttribute are framework-specific.
attributes.TypeAttributes.Take(1),
attribute => Assert.IsType<SerializableAttribute>(attribute));
}
[Fact]
public void GetAttributesForParameter_IncludesTypeAttributes()
{
// Arrange
var parameters = typeof(MethodWithParamAttributesType)
.GetMethod(nameof(MethodWithParamAttributesType.Method))
.GetParameters();
// Act
var attributes = ModelAttributes.GetAttributesForParameter(parameters[2]);
// Assert
Assert.Collection(attributes.Attributes,
attribute => Assert.IsType<BindRequiredAttribute>(attribute),
attribute => Assert.IsType<ClassValidator>(attribute));
Assert.IsType<BindRequiredAttribute>(Assert.Single(attributes.ParameterAttributes));
Assert.Null(attributes.PropertyAttributes);
Assert.IsType<ClassValidator>(Assert.Single(attributes.TypeAttributes));
} }
[ClassValidator] [ClassValidator]
@ -272,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private class ClassValidator : ValidationAttribute private class ClassValidator : ValidationAttribute
{ {
public override Boolean IsValid(Object value) public override bool IsValid(object value)
{ {
return true; return true;
} }
@ -305,7 +335,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private class MethodWithParamAttributesType private class MethodWithParamAttributesType
{ {
[IrrelevantAttribute] // We verify this is ignored [IrrelevantAttribute] // We verify this is ignored
public void Method(object noAttribs, [Required, Range(1, 100)] int validationAttribs) public void Method(
object noAttributes,
[Required, Range(1, 100)] int validationAttributes,
[BindRequired] BaseModel mergedAttributes)
{ {
} }
} }
@ -314,4 +347,4 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
} }
} }
} }

View File

@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
stringLocalizerFactory: null); stringLocalizerFactory: null);
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(new object[] { attribute }, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(new object[] { attribute }));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new[] { dataType, }; var attributes = new[] { dataType, };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { dataType, displayFormat, }; var attributes = new Attribute[] { dataType, displayFormat, };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { editable }; var attributes = new Attribute[] { editable };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateBindingMetadata(context); provider.CreateBindingMetadata(context);
@ -180,7 +180,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { editable }; var attributes = new Attribute[] { editable };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateBindingMetadata(context); provider.CreateBindingMetadata(context);
@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display, displayName }; var attributes = new Attribute[] { display, displayName };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display, displayName }; var attributes = new Attribute[] { display, displayName };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display, displayName }; var attributes = new Attribute[] { display, displayName };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -298,7 +298,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { displayName }; var attributes = new Attribute[] { displayName };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -335,7 +335,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -404,7 +404,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -441,7 +441,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -472,7 +472,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -509,7 +509,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -540,7 +540,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string)); var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -588,7 +588,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { display }; var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(DataAnnotationsMetadataProviderTest)); var key = ModelMetadataIdentity.ForType(typeof(DataAnnotationsMetadataProviderTest));
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(type); var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -671,7 +671,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(type); var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -805,7 +805,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(type); var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -833,7 +833,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new object[0]; var attributes = new object[0];
var key = ModelMetadataIdentity.ForType(type); var key = ModelMetadataIdentity.ForType(type);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
var stringLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict); var stringLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
stringLocalizer stringLocalizer
@ -985,7 +985,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(type); var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -1015,7 +1015,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(typeof(EnumWithDisplayOrder)); var key = ModelMetadataIdentity.ForType(typeof(EnumWithDisplayOrder));
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act // Act
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
@ -1119,7 +1119,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { required }; var attributes = new Attribute[] { required };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act // Act
provider.CreateValidationMetadata(context); provider.CreateValidationMetadata(context);
@ -1141,7 +1141,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { }; var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.ValidationMetadata.IsRequired = initialValue; context.ValidationMetadata.IsRequired = initialValue;
// Act // Act
@ -1164,7 +1164,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { new RequiredAttribute() }; var attributes = new Attribute[] { new RequiredAttribute() };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsBindingRequired = initialValue; context.BindingMetadata.IsBindingRequired = initialValue;
// Act // Act
@ -1187,7 +1187,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attributes = new Attribute[] { }; var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsReadOnly = initialValue; context.BindingMetadata.IsReadOnly = initialValue;
// Act // Act
@ -1208,7 +1208,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attribute = new TestValidationAttribute(); var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute }; var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act // Act
provider.CreateValidationMetadata(context); provider.CreateValidationMetadata(context);
@ -1229,7 +1229,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var attribute = new TestValidationAttribute(); var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute }; var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new ValidationMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.ValidationMetadata.ValidatorMetadata.Add(attribute); context.ValidationMetadata.ValidatorMetadata.Add(attribute);
// Act // Act
@ -1248,7 +1248,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
var key = ModelMetadataIdentity.ForType(typeof(EnumWithLocalizedDisplayNames)); var key = ModelMetadataIdentity.ForType(typeof(EnumWithLocalizedDisplayNames));
var attributes = new object[0]; var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
provider.CreateDisplayMetadata(context); provider.CreateDisplayMetadata(context);
return context.DisplayMetadata.EnumGroupedDisplayNamesAndValues; return context.DisplayMetadata.EnumGroupedDisplayNamesAndValues;
@ -1274,6 +1274,26 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
useStringLocalizer ? stringLocalizerFactory.Object : null); useStringLocalizer ? stringLocalizerFactory.Object : null);
} }
private ModelAttributes GetModelAttributes(IEnumerable<object> typeAttributes)
{
#pragma warning disable CS0618 // Type or member is obsolete
var modelAttributes = new ModelAttributes(typeAttributes);
#pragma warning restore CS0618 // Type or member is obsolete
return modelAttributes;
}
private ModelAttributes GetModelAttributes(
IEnumerable<object> typeAttributes,
IEnumerable<object> propertyAttributes)
{
#pragma warning disable CS0618 // Type or member is obsolete
var modelAttributes = new ModelAttributes(propertyAttributes, typeAttributes);
#pragma warning restore CS0618 // Type or member is obsolete
return modelAttributes;
}
private class KVPEnumGroupAndNameComparer : IEqualityComparer<KeyValuePair<EnumGroupAndName, string>> private class KVPEnumGroupAndNameComparer : IEqualityComparer<KeyValuePair<EnumGroupAndName, string>>
{ {
public static readonly IEqualityComparer<KeyValuePair<EnumGroupAndName, string>> Instance = new KVPEnumGroupAndNameComparer(); public static readonly IEqualityComparer<KeyValuePair<EnumGroupAndName, string>> Instance = new KVPEnumGroupAndNameComparer();

View File

@ -1,6 +1,7 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
typeof(string), typeof(string),
nameof(ClassWithDataMemberIsRequiredTrue.StringProperty), nameof(ClassWithDataMemberIsRequiredTrue.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrue)); typeof(ClassWithDataMemberIsRequiredTrue));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
// Act // Act
provider.CreateBindingMetadata(context); provider.CreateBindingMetadata(context);
@ -51,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
typeof(string), typeof(string),
nameof(ClassWithDataMemberIsRequiredFalse.StringProperty), nameof(ClassWithDataMemberIsRequiredFalse.StringProperty),
typeof(ClassWithDataMemberIsRequiredFalse)); typeof(ClassWithDataMemberIsRequiredFalse));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsBindingRequired = initialValue; context.BindingMetadata.IsBindingRequired = initialValue;
@ -76,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
}; };
var key = ModelMetadataIdentity.ForType(typeof(ClassWithDataMemberIsRequiredTrue)); var key = ModelMetadataIdentity.ForType(typeof(ClassWithDataMemberIsRequiredTrue));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsBindingRequired = initialValue; context.BindingMetadata.IsBindingRequired = initialValue;
@ -99,7 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
typeof(string), typeof(string),
nameof(ClassWithoutAttributes.StringProperty), nameof(ClassWithoutAttributes.StringProperty),
typeof(ClassWithoutAttributes)); typeof(ClassWithoutAttributes));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0], null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue; context.BindingMetadata.IsBindingRequired = initialValue;
@ -127,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
typeof(string), typeof(string),
nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty), nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract)); typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract));
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], attributes, null)); var context = new BindingMetadataProviderContext(key, GetModelAttributes(new object[0], attributes));
context.BindingMetadata.IsBindingRequired = initialValue; context.BindingMetadata.IsBindingRequired = initialValue;
@ -138,6 +139,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
Assert.Equal(initialValue, context.BindingMetadata.IsBindingRequired); Assert.Equal(initialValue, context.BindingMetadata.IsBindingRequired);
} }
private ModelAttributes GetModelAttributes(
IEnumerable<object> typeAttributes,
IEnumerable<object> propertyAttributes)
{
#pragma warning disable CS0618 // Type or member is obsolete
var modelAttributes = new ModelAttributes(propertyAttributes, typeAttributes);
#pragma warning restore CS0618 // Type or member is obsolete
return modelAttributes;
}
[DataContract] [DataContract]
private class ClassWithDataMemberIsRequiredTrue private class ClassWithDataMemberIsRequiredTrue
{ {

View File

@ -1067,7 +1067,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
{ {
return new DefaultMetadataDetails( return new DefaultMetadataDetails(
key, key,
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray(), null, null)); #pragma warning disable CS0618 // Type or member is obsolete
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray()));
#pragma warning restore CS0618 // Type or member is obsolete
} }
return entry; return entry;
@ -1080,10 +1082,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
{ {
return new DefaultMetadataDetails( return new DefaultMetadataDetails(
e.Key, e.Key,
new ModelAttributes(e.ModelAttributes.TypeAttributes, _attributes.Concat(e.ModelAttributes.PropertyAttributes), null)); #pragma warning disable CS0618 // Type or member is obsolete
new ModelAttributes(
_attributes.Concat(e.ModelAttributes.PropertyAttributes),
e.ModelAttributes.TypeAttributes));
#pragma warning restore CS0618 // Type or member is obsolete
}) })
.ToArray(); .ToArray();
} }
} }
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -469,14 +468,17 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task ActionParameter_CustomModelBinder_CanCreateModels_ForParameterlessConstructorTypes() public async Task ActionParameter_CustomModelBinder_CanCreateModels_ForParameterlessConstructorTypes()
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(binderProvider: new CustomComplexTypeModelBinderProvider()); var testContext = ModelBindingTestHelper.GetTestContext();
var parameter = new ParameterDescriptor() var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(
testContext.MvcOptions,
new CustomComplexTypeModelBinderProvider());
var parameter = new ParameterDescriptor
{ {
Name = "prefix", Name = "prefix",
ParameterType = typeof(ClassWithNoDefaultConstructor) ParameterType = typeof(ClassWithNoDefaultConstructor)
}; };
var testContext = ModelBindingTestHelper.GetTestContext();
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -850,7 +852,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
foreach (T t in _original) foreach (var t in _original)
{ {
yield return t; yield return t;
} }
@ -891,4 +893,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
} }
} }
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Xunit; using Xunit;
@ -148,13 +149,63 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
// type. This should behave identically to such an attribute on an action parameter. (Tests such as // type. This should behave identically to such an attribute on an action parameter. (Tests such as
// BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.) // BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.)
// //
// This is a regression test for aspnet/Mvc#4652 // This is a regression test for aspnet/Mvc#4652 and aspnet/Mvc#7595
[Theory] [Theory]
[MemberData(nameof(NullAndEmptyBindingInfo))] [MemberData(nameof(NullAndEmptyBindingInfo))]
public async Task BinderTypeOnParameterType_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo) public async Task BinderTypeOnParameterType_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo)
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); var parameterBinder = ModelBindingTestHelper.GetParameterBinder();
var parameters = typeof(TestController).GetMethod(nameof(TestController.Action)).GetParameters();
var parameter = new ControllerParameterDescriptor
{
Name = "Parameter1",
BindingInfo = bindingInfo,
ParameterInfo = parameters[0],
ParameterType = typeof(Address),
};
var testContext = ModelBindingTestHelper.GetTestContext();
var modelState = testContext.ModelState;
// Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
// Assert
// ModelBindingResult
Assert.True(modelBindingResult.IsModelSet);
// Model
var address = Assert.IsType<Address>(modelBindingResult.Model);
Assert.Equal("SomeStreet", address.Street);
// ModelState
Assert.True(modelState.IsValid);
var kvp = Assert.Single(modelState);
Assert.Equal("Street", kvp.Key);
var entry = kvp.Value;
Assert.NotNull(entry);
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it.
}
// Make sure the metadata is honored when a [ModelBinder] attribute is associated with an action parameter's
// type. This should behave identically to such an attribute on an action parameter. (Tests such as
// BindParameter_WithData_WithPrefix_GetsBound cover associating [ModelBinder] with an action parameter.)
//
// This is a regression test for aspnet/Mvc#4652
[Theory]
[MemberData(nameof(NullAndEmptyBindingInfo))]
public async Task BinderTypeOnParameterType_WithDataEmptyPrefixAndVersion20_GetsBound(
BindingInfo bindingInfo)
{
// Arrange
var testContext = ModelBindingTestHelper.GetTestContext(
// ParameterBinder will use ModelMetadata for typeof(Address), not Parameter1's ParameterInfo.
updateOptions: options => options.AllowValidatingTopLevelNodes = false);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
@ -162,9 +213,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Address), ParameterType = typeof(Address),
}; };
var testContext = ModelBindingTestHelper.GetTestContext();
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -419,5 +467,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
private class TestController
{
public void Action(Address address)
{
}
}
} }
} }

View File

@ -377,15 +377,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty)); request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
request.ContentType = "application/json"; request.ContentType = "application/json";
}); });
testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var addressRequired = ValidationAttributeUtil.GetRequiredErrorMessage("Address"); var addressRequired = ValidationAttributeUtil.GetRequiredErrorMessage("Address");
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -422,14 +418,10 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty)); request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty));
request.ContentType = "application/json"; request.ContentType = "application/json";
}); });
testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
var httpContext = testContext.HttpContext;
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -626,10 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
}, },
options => options.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting); options => options.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting);
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>();
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -793,7 +783,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
.ForProperty<Person6>(nameof(Person6.Address)) .ForProperty<Person6>(nameof(Person6.Address))
.BindingDetails(binding => binding.BindingSource = BindingSource.Body); .BindingDetails(binding => binding.BindingSource = BindingSource.Body);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
request.ContentType = "application/json";
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter-name", Name = "parameter-name",
@ -801,15 +800,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Person6), ParameterType = typeof(Person6),
}; };
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
request.ContentType = "application/json";
});
testContext.MetadataProvider = metadataProvider;
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -839,7 +829,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
.ForType<Address6>() .ForType<Address6>()
.BindingDetails(binding => binding.BindingSource = BindingSource.Body); .BindingDetails(binding => binding.BindingSource = BindingSource.Body);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
request.ContentType = "application/json";
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter-name", Name = "parameter-name",
@ -847,15 +846,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Address6), ParameterType = typeof(Address6),
}; };
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText));
request.ContentType = "application/json";
});
testContext.MetadataProvider = metadataProvider;
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -890,4 +880,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return result; return result;
} }
} }
} }

View File

@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Xunit; using Xunit;
@ -148,13 +147,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
request.ContentType = "application/json"; request.ContentType = "application/json";
}); });
var optionsAccessor = testContext.GetService<IOptions<MvcOptions>>(); testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true;
optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true;
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var valueProvider = await CompositeValueProvider.CreateAsync(testContext); var valueProvider = await CompositeValueProvider.CreateAsync(testContext);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value);
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(testContext, valueProvider, parameter); var modelBindingResult = await parameterBinder.BindModelAsync(testContext, valueProvider, parameter);
@ -1607,7 +1604,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
var model = Assert.IsType<Order8>(modelBindingResult.Model); var model = Assert.IsType<Order8>(modelBindingResult.Model);
Assert.Equal("bill", model.Name); Assert.Equal("bill", model.Name);
Assert.Equal(default(KeyValuePair<string, int>), model.ProductId); Assert.Equal(default, model.ProductId);
Assert.Single(modelState); Assert.Single(modelState);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
@ -1646,7 +1643,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
var model = Assert.IsType<Order8>(modelBindingResult.Model); var model = Assert.IsType<Order8>(modelBindingResult.Model);
Assert.Null(model.Name); Assert.Null(model.Name);
Assert.Equal(default(KeyValuePair<string, int>), model.ProductId); Assert.Equal(default, model.ProductId);
Assert.Empty(modelState); Assert.Empty(modelState);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
@ -2728,4 +2725,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
}); });
} }
} }
} }

View File

@ -67,12 +67,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
{ {
// Arrange // Arrange
var options = new MvcOptions(); var options = new MvcOptions();
var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory());
var modelBinderProvider = new TypeModelBinderProvider(); var modelBinderProvider = new TypeModelBinderProvider();
// Adding a custom model binder for Type to ensure it doesn't get called // Adding a custom model binder for Type to ensure it doesn't get called
options.ModelBinderProviders.Insert(0, modelBinderProvider); options.ModelBinderProviders.Insert(0, modelBinderProvider);
var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory());
setup.Configure(options); setup.Configure(options);
// Remove the ExcludeBindingMetadataProvider // Remove the ExcludeBindingMetadataProvider
@ -84,7 +84,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
} }
} }
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(options); var metadataProvider = TestModelMetadataProvider.CreateProvider(options.ModelMetadataDetailsProviders);
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.Form = new FormCollection(new Dictionary<string, StringValues>
{
{ "name", new[] { "Fred" } },
{ "type", new[] { "SomeType" } },
});
},
metadataProvider: metadataProvider,
mvcOptions: options);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor()
{ {
Name = "Parameter1", Name = "Parameter1",
@ -92,15 +105,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(TypesBundle), ParameterType = typeof(TypesBundle),
}; };
var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.Form = new FormCollection(new Dictionary<string, StringValues>
{
{ "name", new[] { "Fred" } },
{ "type", new[] { "SomeType" } },
});
});
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -145,4 +149,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
} }
} }
} }
} }

View File

@ -196,18 +196,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public async Task GenericModelBinder_BindsCollection_ElementTypeUsesGreedyModelBinder_WithPrefix_Success() public async Task GenericModelBinder_BindsCollection_ElementTypeUsesGreedyModelBinder_WithPrefix_Success()
{ {
// Arrange // Arrange
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(binderProvider: new AddressBinderProvider());
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Address[])
};
// Need to have a key here so that the GenericModelBinder will recurse to bind elements. // Need to have a key here so that the GenericModelBinder will recurse to bind elements.
var testContext = ModelBindingTestHelper.GetTestContext( var testContext = ModelBindingTestHelper.GetTestContext(
request => request.QueryString = new QueryString("?parameter.index=0")); request => request.QueryString = new QueryString("?parameter.index=0"));
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(
testContext.MvcOptions,
new AddressBinderProvider());
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Address[])
};
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -638,4 +640,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
} }
} }
} }

View File

@ -104,17 +104,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
() => $"Hurts when nothing is provided."); () => $"Hurts when nothing is provided.");
})); }));
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.QueryString = new QueryString("?parameter.Value=10");
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter", Name = "parameter",
ParameterType = typeof(KeyValuePair<string, int>) ParameterType = typeof(KeyValuePair<string, int>)
}; };
var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.QueryString = new QueryString("?parameter.Value=10");
});
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -188,18 +191,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
() => $"Hurts when nothing is provided."); () => $"Hurts when nothing is provided.");
})); }));
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.QueryString = new QueryString("?parameter.Key=10");
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter", Name = "parameter",
ParameterType = typeof(KeyValuePair<string, int>) ParameterType = typeof(KeyValuePair<string, int>)
}; };
var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.QueryString = new QueryString("?parameter.Key=10");
});
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -544,4 +549,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Assert.Equal("value2", entry.RawValue); Assert.Equal("value2", entry.RawValue);
} }
} }
} }

View File

@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
{ {
public IModelMetadataProvider MetadataProvider { get; set; } public IModelMetadataProvider MetadataProvider { get; set; }
public MvcOptions MvcOptions { get; set; }
public T GetService<T>() public T GetService<T>()
{ {
return (T)HttpContext.RequestServices.GetService(typeof(T)); return (T)HttpContext.RequestServices.GetService(typeof(T));

View File

@ -26,17 +26,21 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public static ModelBindingTestContext GetTestContext( public static ModelBindingTestContext GetTestContext(
Action<HttpRequest> updateRequest = null, Action<HttpRequest> updateRequest = null,
Action<MvcOptions> updateOptions = null, Action<MvcOptions> updateOptions = null,
ControllerActionDescriptor actionDescriptor = null) ControllerActionDescriptor actionDescriptor = null,
IModelMetadataProvider metadataProvider = null,
MvcOptions mvcOptions = null)
{ {
var httpContext = GetHttpContext(updateRequest, updateOptions); var httpContext = GetHttpContext(metadataProvider, updateRequest, updateOptions, mvcOptions);
var services = httpContext.RequestServices; var services = httpContext.RequestServices;
metadataProvider = metadataProvider ?? services.GetRequiredService<IModelMetadataProvider>();
var options = services.GetRequiredService<IOptions<MvcOptions>>(); var options = services.GetRequiredService<IOptions<MvcOptions>>();
var context = new ModelBindingTestContext() var context = new ModelBindingTestContext
{ {
ActionDescriptor = actionDescriptor ?? new ControllerActionDescriptor(), ActionDescriptor = actionDescriptor ?? new ControllerActionDescriptor(),
HttpContext = httpContext, HttpContext = httpContext,
MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(), MetadataProvider = metadataProvider,
MvcOptions = options.Value,
RouteData = new RouteData(), RouteData = new RouteData(),
ValueProviderFactories = new List<IValueProviderFactory>(options.Value.ValueProviderFactories), ValueProviderFactories = new List<IValueProviderFactory>(options.Value.ValueProviderFactories),
}; };
@ -80,10 +84,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
IModelBinderProvider binderProvider = null, IModelBinderProvider binderProvider = null,
MvcOptions mvcOptions = null) MvcOptions mvcOptions = null)
{ {
var services = GetServices(); var services = GetServices(metadataProvider, mvcOptions: mvcOptions);
var options = mvcOptions != null var options = services.GetRequiredService<IOptions<MvcOptions>>();
? Options.Create(mvcOptions)
: services.GetRequiredService<IOptions<MvcOptions>>();
if (binderProvider != null) if (binderProvider != null)
{ {
@ -102,15 +104,15 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
public static IModelBinderFactory GetModelBinderFactory( public static IModelBinderFactory GetModelBinderFactory(
IModelMetadataProvider metadataProvider, IModelMetadataProvider metadataProvider,
IOptions<MvcOptions> options = null) IServiceProvider services = null)
{ {
var services = GetServices(); if (services == null)
if (options == null)
{ {
options = services.GetRequiredService<IOptions<MvcOptions>>(); services = GetServices(metadataProvider);
} }
var options = services.GetRequiredService<IOptions<MvcOptions>>();
return new ModelBinderFactory(metadataProvider, options, services); return new ModelBinderFactory(metadataProvider, options, services);
} }
@ -136,23 +138,52 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
} }
private static HttpContext GetHttpContext( private static HttpContext GetHttpContext(
IModelMetadataProvider metadataProvider,
Action<HttpRequest> updateRequest = null, Action<HttpRequest> updateRequest = null,
Action<MvcOptions> updateOptions = null) Action<MvcOptions> updateOptions = null,
MvcOptions mvcOptions = null)
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Features.Set<IHttpRequestLifetimeFeature>(new CancellableRequestLifetimeFeature()); httpContext.Features.Set<IHttpRequestLifetimeFeature>(new CancellableRequestLifetimeFeature());
updateRequest?.Invoke(httpContext.Request); updateRequest?.Invoke(httpContext.Request);
httpContext.RequestServices = GetServices(updateOptions); httpContext.RequestServices = GetServices(metadataProvider, updateOptions, mvcOptions);
return httpContext; return httpContext;
} }
public static IServiceProvider GetServices(Action<MvcOptions> updateOptions = null) public static IServiceProvider GetServices(
IModelMetadataProvider metadataProvider,
Action<MvcOptions> updateOptions = null,
MvcOptions mvcOptions = null)
{ {
var serviceCollection = new ServiceCollection(); var serviceCollection = new ServiceCollection();
serviceCollection.AddAuthorization();
serviceCollection.AddSingleton(new ApplicationPartManager()); serviceCollection.AddSingleton(new ApplicationPartManager());
if (metadataProvider != null)
{
serviceCollection.AddSingleton(metadataProvider);
}
else if (updateOptions != null || mvcOptions != null)
{
serviceCollection.AddSingleton(services =>
{
var optionsAccessor = services.GetRequiredService<IOptions<MvcOptions>>();
return TestModelMetadataProvider.CreateProvider(optionsAccessor.Value.ModelMetadataDetailsProviders);
});
}
else
{
serviceCollection.AddSingleton<IModelMetadataProvider>(services =>
{
return TestModelMetadataProvider.CreateDefaultProvider();
});
}
if (mvcOptions != null)
{
serviceCollection.AddSingleton(Options.Create(mvcOptions));
}
serviceCollection serviceCollection
.AddMvc() .AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1); .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

View File

@ -1,23 +1,60 @@
// 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.IntegrationTests namespace Microsoft.AspNetCore.Mvc.IntegrationTests
{ {
public static class ParameterBinderExtensions public static class ParameterBinderExtensions
{ {
public static async Task<ModelBindingResult> BindModelAsync( public static Task<ModelBindingResult> BindModelAsync(
this ParameterBinder parameterBinder, this ParameterBinder parameterBinder,
ParameterDescriptor parameter, ParameterDescriptor parameter,
ControllerContext context) ControllerContext context)
{ {
var valueProvider = await CompositeValueProvider.CreateAsync(context); var optionsAccessor = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
Assert.NotNull(optionsAccessor?.Value); // Guard
return await parameterBinder.BindModelAsync(context, valueProvider, parameter); var modelMetadataProvider = context.HttpContext.RequestServices.GetService<IModelMetadataProvider>();
Assert.NotNull(modelMetadataProvider); // Guard
// Imitate a bit of ControllerBinderDelegateProvider and PageBinderFactory
ParameterInfo parameterInfo;
if (parameter is ControllerParameterDescriptor controllerParameterDescriptor)
{
parameterInfo = controllerParameterDescriptor.ParameterInfo;
}
else if (parameter is HandlerParameterDescriptor handlerParameterDescriptor)
{
parameterInfo = handlerParameterDescriptor.ParameterInfo;
}
else
{
parameterInfo = null;
}
ModelMetadata metadata;
if (optionsAccessor.Value.AllowValidatingTopLevelNodes &&
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
parameterInfo != null)
{
metadata = modelMetadataProviderBase.GetMetadataForParameter(parameterInfo);
}
else
{
metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
}
return parameterBinder.BindModelAsync(parameter, context, modelMetadataProvider, metadata);
} }
public static async Task<ModelBindingResult> BindModelAsync( public static async Task<ModelBindingResult> BindModelAsync(
this ParameterBinder parameterBinder, this ParameterBinder parameterBinder,
ParameterDescriptor parameter, ParameterDescriptor parameter,
@ -26,8 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ModelMetadata modelMetadata) ModelMetadata modelMetadata)
{ {
var valueProvider = await CompositeValueProvider.CreateAsync(context); var valueProvider = await CompositeValueProvider.CreateAsync(context);
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(
var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(modelMetadataProvider); modelMetadataProvider,
context.HttpContext.RequestServices);
var modelBinder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext var modelBinder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
{ {

View File

@ -205,7 +205,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
.ForProperty<Person>(nameof(Person.Service)) .ForProperty<Person>(nameof(Person.Service))
.BindingDetails(binding => binding.BindingSource = BindingSource.Services); .BindingDetails(binding => binding.BindingSource = BindingSource.Services);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter-name", Name = "parameter-name",
@ -213,10 +215,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Person), ParameterType = typeof(Person),
}; };
var testContext = ModelBindingTestHelper.GetTestContext();
testContext.MetadataProvider = metadataProvider;
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -245,7 +243,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
.ForType<JsonOutputFormatter>() .ForType<JsonOutputFormatter>()
.BindingDetails(binding => binding.BindingSource = BindingSource.Services); .BindingDetails(binding => binding.BindingSource = BindingSource.Services);
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider); var testContext = ModelBindingTestHelper.GetTestContext(metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "parameter-name", Name = "parameter-name",
@ -253,10 +253,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(JsonOutputFormatter), ParameterType = typeof(JsonOutputFormatter),
}; };
var testContext = ModelBindingTestHelper.GetTestContext();
testContext.MetadataProvider = metadataProvider;
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -268,4 +264,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
Assert.Empty(modelState); Assert.Empty(modelState);
} }
} }
} }

View File

@ -2,7 +2,6 @@
// 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.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -304,7 +303,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
binding.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor( binding.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
(value) => $"Hmm, '{ value }' is not a valid value."); (value) => $"Hmm, '{ value }' is not a valid value.");
}); });
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.QueryString = QueryString.Create("Parameter1", "abcd");
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor()
{ {
Name = "Parameter1", Name = "Parameter1",
@ -312,13 +320,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = parameterType ParameterType = parameterType
}; };
var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.QueryString = QueryString.Create("Parameter1", "abcd");
});
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);
@ -405,8 +406,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
binding.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( binding.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
value => $"Hurts when '{ value }' is provided."); value => $"Hurts when '{ value }' is provided.");
}); });
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(metadataProvider);
var testContext = ModelBindingTestHelper.GetTestContext(
request =>
{
request.QueryString = QueryString.Create("Parameter1", string.Empty);
},
metadataProvider: metadataProvider);
var modelState = testContext.ModelState;
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var parameter = new ParameterDescriptor var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
@ -414,11 +423,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = parameterType ParameterType = parameterType
}; };
var testContext = ModelBindingTestHelper.GetTestContext(request =>
{
request.QueryString = QueryString.Create("Parameter1", string.Empty);
});
var modelState = testContext.ModelState;
// Act // Act
var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext);

View File

@ -1608,7 +1608,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
ParameterType = typeof(Order11) ParameterType = typeof(Order11)
}; };
MvcOptions testOptions = null;
var input = "{\"Zip\":\"47\"}"; var input = "{\"Zip\":\"47\"}";
var testContext = ModelBindingTestHelper.GetTestContext( var testContext = ModelBindingTestHelper.GetTestContext(
request => request =>
@ -1621,10 +1620,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
options => options =>
{ {
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Address))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(Address)));
testOptions = options;
}); });
var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testOptions); var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices);
var modelState = testContext.ModelState; var modelState = testContext.ModelState;
// Act // Act

View File

@ -26,7 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
var detailsProvider = new DefaultCompositeMetadataDetailsProvider( var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>()); Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(DateTime)); var key = ModelMetadataIdentity.ForType(typeof(DateTime));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null)); #pragma warning disable CS0618 // Type or member is obsolete
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
#pragma warning restore CS0618 // Type or member is obsolete
var provider = new EmptyModelMetadataProvider(); var provider = new EmptyModelMetadataProvider();
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache); var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);