diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/ModelAttributes.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/ModelAttributes.cs index 0276163272..e5b59933ae 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/ModelAttributes.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/ModelAttributes.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; 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. /// Otherwise, null. /// - public ModelAttributes(IEnumerable typeAttributes, IEnumerable propertyAttributes, IEnumerable parameterAttributes) + internal ModelAttributes( + IEnumerable typeAttributes, + IEnumerable propertyAttributes, + IEnumerable parameterAttributes) { if (propertyAttributes != null) { @@ -73,7 +74,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding else if (parameterAttributes != null) { // 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) { @@ -89,7 +97,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding /// /// 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 . + /// on the property definition are before those on the property's . If this instance + /// represents the attributes for a parameter, the attributes on the parameter definition are before those on + /// the parameter's . /// public IReadOnlyList Attributes { get; } @@ -106,10 +116,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding public IReadOnlyList ParameterAttributes { get; } /// - /// Gets the set of attributes on the . If this instance represents a property, - /// then contains attributes retrieved from - /// . If this instance represents a parameter, then - /// the value is null. + /// Gets the set of attributes on the . If this instance represents a property, then + /// contains attributes retrieved from . + /// If this instance represents a parameter, then contains attributes retrieved from + /// . /// public IReadOnlyList TypeAttributes { get; } @@ -120,7 +130,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding /// /// A for which attributes need to be resolved. /// - /// A instance with the attributes of the property. + /// + /// A instance with the attributes of the property and its . + /// public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property) { 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); } /// @@ -170,18 +182,27 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding attributes = attributes.Concat(metadataType.GetTypeInfo().GetCustomAttributes()); } - return new ModelAttributes(attributes, null, null); + return new ModelAttributes(attributes, propertyAttributes: null, parameterAttributes: null); } /// /// Gets the attributes for the given . /// - /// The for which attributes need to be resolved. + /// + /// The for which attributes need to be resolved. /// - /// A instance with the attributes of the . + /// + /// A instance with the attributes of the parameter and its . + /// 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) diff --git a/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs index 3c83f948d8..cdd8d4570f 100644 --- a/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Abstractions.Test/ModelBinding/BindingInfoTest.cs @@ -176,7 +176,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding { // Arrange var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), }; - var modelAttributes = new ModelAttributes(Enumerable.Empty(), null, null); var propertyFilterProvider = Mock.Of(); var modelType = typeof(Guid); var provider = new TestModelMetadataProvider(); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultBindingMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultBindingMetadataProviderTest.cs index 327f8aa66f..7464e6203a 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultBindingMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultBindingMetadataProviderTest.cs @@ -279,7 +279,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); @@ -302,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); @@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); @@ -348,7 +348,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); @@ -371,7 +371,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); @@ -395,7 +395,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal var context = new BindingMetadataProviderContext( ModelMetadataIdentity.ForParameter(ParameterInfos.SampleParameterInfo), - new ModelAttributes(null, null, parameterAttributes)); + new ModelAttributes(Array.Empty(), null, parameterAttributes)); var provider = new DefaultBindingMetadataProvider(); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs index 70c3edc6a2..fe6a060d92 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/JQueryQueryStringValueProviderFactoryTest.cs @@ -17,12 +17,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test { private static readonly Dictionary _backingStore = new Dictionary { - // 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 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" } }, { "[]property1", new[] { "found" } }, { "property2[]", new[] { "found" } }, diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/DefaultModelMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/DefaultModelMetadataProviderTest.cs index fd5e9fa75d..4286f263c9 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/DefaultModelMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/DefaultModelMetadataProviderTest.cs @@ -212,7 +212,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata // Assert var defaultMetadata = Assert.IsType(metadata); - Assert.Empty(defaultMetadata.Attributes.Attributes); + + // Not exactly "no attributes" due to SerializableAttribute on object. + Assert.IsType(Assert.Single(defaultMetadata.Attributes.Attributes)); } [Fact] @@ -229,11 +231,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata // Assert var defaultMetadata = Assert.IsType(metadata); - Assert.Equal(2, defaultMetadata.Attributes.Attributes.Count); - var attribute1 = Assert.IsType(defaultMetadata.Attributes.Attributes[0]); - Assert.Equal("ParamAttrib1", attribute1.Value); - var attribute2 = Assert.IsType(defaultMetadata.Attributes.Attributes[1]); - Assert.Equal("ParamAttrib2", attribute2.Value); + Assert.Collection( + // Take(2) to ignore SerializableAttribute on object. + defaultMetadata.Attributes.Attributes.Take(2), + attribute => + { + var modelAttribute = Assert.IsType(attribute); + Assert.Equal("ParamAttrib1", modelAttribute.Value); + }, + attribute => + { + var modelAttribute = Assert.IsType(attribute); + Assert.Equal("ParamAttrib2", modelAttribute.Value); + }); } [Fact] diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/ModelAttributesTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/ModelAttributesTest.cs index 69c146726e..3b0b3b0d92 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/ModelAttributesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/ModelAttributesTest.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Xunit; namespace Microsoft.AspNetCore.Mvc.ModelBinding @@ -188,10 +189,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding .GetParameters()[0]); // Assert - Assert.Empty(attributes.Attributes); + // Not exactly "no attributes" due to SerializableAttribute on object. + Assert.IsType(Assert.Single(attributes.Attributes)); Assert.Empty(attributes.ParameterAttributes); - Assert.Null(attributes.TypeAttributes); Assert.Null(attributes.PropertyAttributes); + Assert.Equal(attributes.Attributes, attributes.TypeAttributes); } [Fact] @@ -204,12 +206,40 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding .GetParameters()[1]); // Assert - Assert.IsType(attributes.Attributes[0]); - Assert.IsType(attributes.Attributes[1]); - Assert.IsType(attributes.ParameterAttributes[0]); - Assert.IsType(attributes.ParameterAttributes[1]); - Assert.Null(attributes.TypeAttributes); + Assert.Collection( + // Take(2) to ignore ComVisibleAttribute, SerializableAttribute, ... on int. + attributes.Attributes.Take(2), + attribute => Assert.IsType(attribute), + attribute => Assert.IsType(attribute)); + Assert.Collection( + attributes.ParameterAttributes, + attribute => Assert.IsType(attribute), + attribute => Assert.IsType(attribute)); 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(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(attribute), + attribute => Assert.IsType(attribute)); + Assert.IsType(Assert.Single(attributes.ParameterAttributes)); + Assert.Null(attributes.PropertyAttributes); + Assert.IsType(Assert.Single(attributes.TypeAttributes)); } [ClassValidator] @@ -272,7 +302,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding private class ClassValidator : ValidationAttribute { - public override Boolean IsValid(Object value) + public override bool IsValid(object value) { return true; } @@ -305,7 +335,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding private class MethodWithParamAttributesType { [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 { } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs index 533fffd03c..ac90d1f155 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal stringLocalizerFactory: null); 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 provider.CreateDisplayMetadata(context); @@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new[] { dataType, }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { dataType, displayFormat, }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { editable }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateBindingMetadata(context); @@ -180,7 +180,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { editable }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new BindingMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateBindingMetadata(context); @@ -207,7 +207,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display, displayName }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display, displayName }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -261,7 +261,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display, displayName }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -298,7 +298,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { displayName }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -335,7 +335,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -367,7 +367,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -404,7 +404,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -441,7 +441,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -472,7 +472,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -509,7 +509,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -540,7 +540,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(string)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -588,7 +588,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { display }; var key = ModelMetadataIdentity.ForType(typeof(DataAnnotationsMetadataProviderTest)); - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -671,7 +671,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -805,7 +805,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -833,7 +833,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new object[0]; 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(MockBehavior.Strict); stringLocalizer @@ -985,7 +985,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -1015,7 +1015,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(typeof(EnumWithDisplayOrder)); var attributes = new object[0]; - var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes, null, null)); + var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes)); // Act provider.CreateDisplayMetadata(context); @@ -1119,7 +1119,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { required }; 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 provider.CreateValidationMetadata(context); @@ -1141,7 +1141,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { }; 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; // Act @@ -1164,7 +1164,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { new RequiredAttribute() }; 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; // Act @@ -1187,7 +1187,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attributes = new Attribute[] { }; 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; // Act @@ -1208,7 +1208,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attribute = new TestValidationAttribute(); var attributes = new Attribute[] { attribute }; 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 provider.CreateValidationMetadata(context); @@ -1229,7 +1229,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var attribute = new TestValidationAttribute(); var attributes = new Attribute[] { attribute }; 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); // Act @@ -1248,7 +1248,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var key = ModelMetadataIdentity.ForType(typeof(EnumWithLocalizedDisplayNames)); 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); return context.DisplayMetadata.EnumGroupedDisplayNamesAndValues; @@ -1274,6 +1274,26 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal useStringLocalizer ? stringLocalizerFactory.Object : null); } + private ModelAttributes GetModelAttributes(IEnumerable 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 typeAttributes, + IEnumerable 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> { public static readonly IEqualityComparer> Instance = new KVPEnumGroupAndNameComparer(); diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataMemberRequiredBindingMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataMemberRequiredBindingMetadataProviderTest.cs index 48dc323474..499808aa94 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataMemberRequiredBindingMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataMemberRequiredBindingMetadataProviderTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Runtime.Serialization; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; @@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal typeof(string), nameof(ClassWithDataMemberIsRequiredTrue.StringProperty), 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 provider.CreateBindingMetadata(context); @@ -51,7 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal typeof(string), nameof(ClassWithDataMemberIsRequiredFalse.StringProperty), 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; @@ -76,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal }; 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; @@ -99,7 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal typeof(string), nameof(ClassWithoutAttributes.StringProperty), 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; @@ -127,7 +128,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal typeof(string), nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty), 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; @@ -138,6 +139,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal Assert.Equal(initialValue, context.BindingMetadata.IsBindingRequired); } + private ModelAttributes GetModelAttributes( + IEnumerable typeAttributes, + IEnumerable 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] private class ClassWithDataMemberIsRequiredTrue { diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs index 62602e8c3e..4002e30c99 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs @@ -1067,7 +1067,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal { return new DefaultMetadataDetails( 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; @@ -1080,10 +1082,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal { return new DefaultMetadataDetails( 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(); } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs index 4e349de9b9..fcd5923101 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ActionParametersIntegrationTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; @@ -469,14 +468,17 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests public async Task ActionParameter_CustomModelBinder_CanCreateModels_ForParameterlessConstructorTypes() { // Arrange - var parameterBinder = ModelBindingTestHelper.GetParameterBinder(binderProvider: new CustomComplexTypeModelBinderProvider()); - var parameter = new ParameterDescriptor() + var testContext = ModelBindingTestHelper.GetTestContext(); + var modelState = testContext.ModelState; + var parameterBinder = ModelBindingTestHelper.GetParameterBinder( + testContext.MvcOptions, + new CustomComplexTypeModelBinderProvider()); + + var parameter = new ParameterDescriptor { Name = "prefix", ParameterType = typeof(ClassWithNoDefaultConstructor) }; - var testContext = ModelBindingTestHelper.GetTestContext(); - var modelState = testContext.ModelState; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -850,7 +852,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests public IEnumerator GetEnumerator() { - foreach (T t in _original) + foreach (var t in _original) { yield return t; } @@ -891,4 +893,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs index 688cbbffef..b35b180597 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BinderTypeBasedModelBinderIntegrationTest.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Testing; 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 // 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] [MemberData(nameof(NullAndEmptyBindingInfo))] public async Task BinderTypeOnParameterType_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo) { // Arrange 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
(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 { Name = "Parameter1", @@ -162,9 +213,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = typeof(Address), }; - var testContext = ModelBindingTestHelper.GetTestContext(); - var modelState = testContext.ModelState; - // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -419,5 +467,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests return Task.CompletedTask; } } + + private class TestController + { + public void Action(Address address) + { + } + } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs index 2257eb6eb1..e4ee6a548c 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BodyValidationIntegrationTests.cs @@ -377,15 +377,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests request.Body = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty)); request.ContentType = "application/json"; }); + testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true; var modelState = testContext.ModelState; - var addressRequired = ValidationAttributeUtil.GetRequiredErrorMessage("Address"); - - var optionsAccessor = testContext.GetService>(); - optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true; - - var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value); + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices); // Act 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.ContentType = "application/json"; }); + testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true; - var httpContext = testContext.HttpContext; var modelState = testContext.ModelState; - - var optionsAccessor = testContext.GetService>(); - optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true; - - var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value); + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices); // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -626,10 +618,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests }, options => options.AllowEmptyInputInBodyModelBinding = allowEmptyInputInBodyModelBindingSetting); - var optionsAccessor = testContext.GetService>(); var modelState = testContext.ModelState; - - var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value); + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices); // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -793,7 +783,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests .ForProperty(nameof(Person6.Address)) .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 { Name = "parameter-name", @@ -801,15 +800,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests 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 var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -839,7 +829,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests .ForType() .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 { Name = "parameter-name", @@ -847,15 +846,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests 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 var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -890,4 +880,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests return result; } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ComplexTypeModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ComplexTypeModelBinderIntegrationTest.cs index 5a017bd1d0..e3f693f58a 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ComplexTypeModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ComplexTypeModelBinderIntegrationTest.cs @@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Testing; -using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Xunit; @@ -148,13 +147,11 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests request.ContentType = "application/json"; }); - var optionsAccessor = testContext.GetService>(); - optionsAccessor.Value.AllowEmptyInputInBodyModelBinding = true; + testContext.MvcOptions.AllowEmptyInputInBodyModelBinding = true; var modelState = testContext.ModelState; var valueProvider = await CompositeValueProvider.CreateAsync(testContext); - - var parameterBinder = ModelBindingTestHelper.GetParameterBinder(optionsAccessor.Value); + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices); // Act var modelBindingResult = await parameterBinder.BindModelAsync(testContext, valueProvider, parameter); @@ -1607,7 +1604,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests var model = Assert.IsType(modelBindingResult.Model); Assert.Equal("bill", model.Name); - Assert.Equal(default(KeyValuePair), model.ProductId); + Assert.Equal(default, model.ProductId); Assert.Single(modelState); Assert.Equal(0, modelState.ErrorCount); @@ -1646,7 +1643,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests var model = Assert.IsType(modelBindingResult.Model); Assert.Null(model.Name); - Assert.Equal(default(KeyValuePair), model.ProductId); + Assert.Equal(default, model.ProductId); Assert.Empty(modelState); Assert.Equal(0, modelState.ErrorCount); @@ -2728,4 +2725,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests }); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ExcludeBindingMetadataProviderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ExcludeBindingMetadataProviderIntegrationTest.cs index 8617f8551c..185f828a86 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ExcludeBindingMetadataProviderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ExcludeBindingMetadataProviderIntegrationTest.cs @@ -67,12 +67,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests { // Arrange var options = new MvcOptions(); - var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()); var modelBinderProvider = new TypeModelBinderProvider(); // Adding a custom model binder for Type to ensure it doesn't get called options.ModelBinderProviders.Insert(0, modelBinderProvider); + var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()); setup.Configure(options); // 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 + { + { "name", new[] { "Fred" } }, + { "type", new[] { "SomeType" } }, + }); + }, + metadataProvider: metadataProvider, + mvcOptions: options); + + var parameterBinder = ModelBindingTestHelper.GetParameterBinder(testContext.HttpContext.RequestServices); var parameter = new ParameterDescriptor() { Name = "Parameter1", @@ -92,15 +105,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = typeof(TypesBundle), }; - var testContext = ModelBindingTestHelper.GetTestContext(request => - { - request.Form = new FormCollection(new Dictionary - { - { "name", new[] { "Fred" } }, - { "type", new[] { "SomeType" } }, - }); - }); - // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -145,4 +149,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs index acb91f6ed5..22a7be2d24 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/GenericModelBinderIntegrationTest.cs @@ -196,18 +196,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests public async Task GenericModelBinder_BindsCollection_ElementTypeUsesGreedyModelBinder_WithPrefix_Success() { // 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. var testContext = ModelBindingTestHelper.GetTestContext( request => request.QueryString = new QueryString("?parameter.index=0")); var modelState = testContext.ModelState; + var parameterBinder = ModelBindingTestHelper.GetParameterBinder( + testContext.MvcOptions, + new AddressBinderProvider()); + + var parameter = new ParameterDescriptor() + { + Name = "parameter", + ParameterType = typeof(Address[]) + }; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -638,4 +640,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests Assert.True(modelState.IsValid); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs index 79c0803f15..42b6b827f9 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/KeyValuePairModelBinderIntegrationTest.cs @@ -104,17 +104,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests () => $"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 { Name = "parameter", ParameterType = typeof(KeyValuePair) }; - var testContext = ModelBindingTestHelper.GetTestContext(request => - { - request.QueryString = new QueryString("?parameter.Value=10"); - }); - var modelState = testContext.ModelState; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -188,18 +191,20 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests () => $"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 { Name = "parameter", ParameterType = typeof(KeyValuePair) }; - var testContext = ModelBindingTestHelper.GetTestContext(request => - { - request.QueryString = new QueryString("?parameter.Key=10"); - }); - var modelState = testContext.ModelState; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -544,4 +549,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests Assert.Equal("value2", entry.RawValue); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestContext.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestContext.cs index 2457a8ca1e..4cf87f2300 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestContext.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestContext.cs @@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests { public IModelMetadataProvider MetadataProvider { get; set; } + public MvcOptions MvcOptions { get; set; } + public T GetService() { return (T)HttpContext.RequestServices.GetService(typeof(T)); diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs index 00d2d6046d..6aa9da7cf0 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ModelBindingTestHelper.cs @@ -26,17 +26,21 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests public static ModelBindingTestContext GetTestContext( Action updateRequest = null, Action 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; + metadataProvider = metadataProvider ?? services.GetRequiredService(); var options = services.GetRequiredService>(); - var context = new ModelBindingTestContext() + var context = new ModelBindingTestContext { ActionDescriptor = actionDescriptor ?? new ControllerActionDescriptor(), HttpContext = httpContext, - MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(), + MetadataProvider = metadataProvider, + MvcOptions = options.Value, RouteData = new RouteData(), ValueProviderFactories = new List(options.Value.ValueProviderFactories), }; @@ -80,10 +84,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests IModelBinderProvider binderProvider = null, MvcOptions mvcOptions = null) { - var services = GetServices(); - var options = mvcOptions != null - ? Options.Create(mvcOptions) - : services.GetRequiredService>(); + var services = GetServices(metadataProvider, mvcOptions: mvcOptions); + var options = services.GetRequiredService>(); if (binderProvider != null) { @@ -102,15 +104,15 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests public static IModelBinderFactory GetModelBinderFactory( IModelMetadataProvider metadataProvider, - IOptions options = null) + IServiceProvider services = null) { - var services = GetServices(); - - if (options == null) + if (services == null) { - options = services.GetRequiredService>(); + services = GetServices(metadataProvider); } + var options = services.GetRequiredService>(); + return new ModelBinderFactory(metadataProvider, options, services); } @@ -136,23 +138,52 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests } private static HttpContext GetHttpContext( + IModelMetadataProvider metadataProvider, Action updateRequest = null, - Action updateOptions = null) + Action updateOptions = null, + MvcOptions mvcOptions = null) { var httpContext = new DefaultHttpContext(); httpContext.Features.Set(new CancellableRequestLifetimeFeature()); updateRequest?.Invoke(httpContext.Request); - httpContext.RequestServices = GetServices(updateOptions); + httpContext.RequestServices = GetServices(metadataProvider, updateOptions, mvcOptions); return httpContext; } - public static IServiceProvider GetServices(Action updateOptions = null) + public static IServiceProvider GetServices( + IModelMetadataProvider metadataProvider, + Action updateOptions = null, + MvcOptions mvcOptions = null) { var serviceCollection = new ServiceCollection(); - serviceCollection.AddAuthorization(); serviceCollection.AddSingleton(new ApplicationPartManager()); + if (metadataProvider != null) + { + serviceCollection.AddSingleton(metadataProvider); + } + else if (updateOptions != null || mvcOptions != null) + { + serviceCollection.AddSingleton(services => + { + var optionsAccessor = services.GetRequiredService>(); + return TestModelMetadataProvider.CreateProvider(optionsAccessor.Value.ModelMetadataDetailsProviders); + }); + } + else + { + serviceCollection.AddSingleton(services => + { + return TestModelMetadataProvider.CreateDefaultProvider(); + }); + } + + if (mvcOptions != null) + { + serviceCollection.AddSingleton(Options.Create(mvcOptions)); + } + serviceCollection .AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ParameterBinderExtensions.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ParameterBinderExtensions.cs index 8d839c83a3..2cf116e039 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ParameterBinderExtensions.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ParameterBinderExtensions.cs @@ -1,23 +1,60 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Controllers; 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 { public static class ParameterBinderExtensions { - public static async Task BindModelAsync( + public static Task BindModelAsync( this ParameterBinder parameterBinder, - ParameterDescriptor parameter, + ParameterDescriptor parameter, ControllerContext context) { - var valueProvider = await CompositeValueProvider.CreateAsync(context); - - return await parameterBinder.BindModelAsync(context, valueProvider, parameter); + var optionsAccessor = context.HttpContext.RequestServices.GetService>(); + Assert.NotNull(optionsAccessor?.Value); // Guard + var modelMetadataProvider = context.HttpContext.RequestServices.GetService(); + 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 BindModelAsync( this ParameterBinder parameterBinder, ParameterDescriptor parameter, @@ -26,8 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ModelMetadata modelMetadata) { var valueProvider = await CompositeValueProvider.CreateAsync(context); - - var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory(modelMetadataProvider); + var modelBinderFactory = ModelBindingTestHelper.GetModelBinderFactory( + modelMetadataProvider, + context.HttpContext.RequestServices); var modelBinder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ServicesModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ServicesModelBinderIntegrationTest.cs index 8d3f83f68f..1ea4cbf95b 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ServicesModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ServicesModelBinderIntegrationTest.cs @@ -205,7 +205,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests .ForProperty(nameof(Person.Service)) .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 { Name = "parameter-name", @@ -213,10 +215,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = typeof(Person), }; - var testContext = ModelBindingTestHelper.GetTestContext(); - testContext.MetadataProvider = metadataProvider; - var modelState = testContext.ModelState; - // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -245,7 +243,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests .ForType() .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 { Name = "parameter-name", @@ -253,10 +253,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = typeof(JsonOutputFormatter), }; - var testContext = ModelBindingTestHelper.GetTestContext(); - testContext.MetadataProvider = metadataProvider; - var modelState = testContext.ModelState; - // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -268,4 +264,4 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests Assert.Empty(modelState); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs index 0d5cede662..420939c66b 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -304,7 +303,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests binding.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor( (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() { Name = "Parameter1", @@ -312,13 +320,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = parameterType }; - var testContext = ModelBindingTestHelper.GetTestContext(request => - { - request.QueryString = QueryString.Create("Parameter1", "abcd"); - }); - - var modelState = testContext.ModelState; - // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); @@ -405,8 +406,16 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests binding.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( 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 { Name = "Parameter1", @@ -414,11 +423,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = parameterType }; - var testContext = ModelBindingTestHelper.GetTestContext(request => - { - request.QueryString = QueryString.Create("Parameter1", string.Empty); - }); - var modelState = testContext.ModelState; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ValidationIntegrationTests.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ValidationIntegrationTests.cs index 036f758433..e5df3d1233 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ValidationIntegrationTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/ValidationIntegrationTests.cs @@ -1608,7 +1608,6 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests ParameterType = typeof(Order11) }; - MvcOptions testOptions = null; var input = "{\"Zip\":\"47\"}"; var testContext = ModelBindingTestHelper.GetTestContext( request => @@ -1621,10 +1620,9 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests options => { 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; // Act diff --git a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/FormatWeekHelperTest.cs b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/FormatWeekHelperTest.cs index 9586bf664e..97f45f0c0f 100644 --- a/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/FormatWeekHelperTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.ViewFeatures.Test/Internal/FormatWeekHelperTest.cs @@ -26,7 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal var detailsProvider = new DefaultCompositeMetadataDetailsProvider( Enumerable.Empty()); 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 metadata = new DefaultModelMetadata(provider, detailsProvider, cache);