diff --git a/Mvc.sln b/Mvc.sln index 8c6313883b..807bbd13c7 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}" EndProject diff --git a/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/DataAnnotationsMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/DataAnnotationsMetadataProvider.cs index 44503ed7b7..2cbb4f2ac4 100644 --- a/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/DataAnnotationsMetadataProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/DataAnnotationsMetadataProvider.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.Extensions.Localization; namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal { @@ -20,6 +21,13 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal IDisplayMetadataProvider, IValidationMetadataProvider { + private readonly IStringLocalizerFactory _stringLocalizerFactory; + + public DataAnnotationsMetadataProvider(IStringLocalizerFactory stringLocalizerFactory) + { + _stringLocalizerFactory = stringLocalizerFactory; + } + /// public void CreateBindingMetadata(BindingMetadataProviderContext context) { @@ -80,11 +88,23 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal displayMetadata.DataTypeName = DataType.Html.ToString(); } + + var containerType = context.Key.ContainerType ?? context.Key.ModelType; + var localizer = _stringLocalizerFactory?.Create(containerType); + // Description if (displayAttribute != null) { - displayMetadata.Description = () => displayAttribute.GetDescription(); - displayMetadata.Placeholder = () => displayAttribute.GetPrompt(); + if (localizer != null && + !string.IsNullOrEmpty(displayAttribute.Description) && + displayAttribute.ResourceType == null) + { + displayMetadata.Description = () => localizer[displayAttribute.Description]; + } + else + { + displayMetadata.Description = () => displayAttribute.GetDescription(); + } } // DisplayFormatString @@ -96,7 +116,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal // DisplayName if (displayAttribute != null) { - displayMetadata.DisplayName = () => displayAttribute.GetName(); + if (localizer != null && + !string.IsNullOrEmpty(displayAttribute.Name) && + displayAttribute.ResourceType == null) + { + displayMetadata.DisplayName = () => localizer[displayAttribute.Name]; + } + else + { + displayMetadata.DisplayName = () => displayAttribute.GetName(); + } } // EditFormatString @@ -189,6 +218,21 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal displayMetadata.Order = displayAttribute.GetOrder().Value; } + // Placeholder + if (displayAttribute != null) + { + if (localizer != null && + !string.IsNullOrEmpty(displayAttribute.Prompt) && + displayAttribute.ResourceType == null) + { + displayMetadata.Placeholder = () => localizer[displayAttribute.Prompt]; + } + else + { + displayMetadata.Placeholder = () => displayAttribute.GetPrompt(); + } + } + // ShowForDisplay if (scaffoldColumnAttribute != null) { diff --git a/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/MvcDataAnnotationsMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/MvcDataAnnotationsMvcOptionsSetup.cs index 0d80f92f6e..9c846ffd2a 100644 --- a/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/MvcDataAnnotationsMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/MvcDataAnnotationsMvcOptionsSetup.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal var stringLocalizerFactory = serviceProvider.GetService(); var validationAttributeAdapterProvider = serviceProvider.GetRequiredService(); - options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider()); + options.ModelMetadataDetailsProviders.Add(new DataAnnotationsMetadataProvider(stringLocalizerFactory)); options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider( validationAttributeAdapterProvider, diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs index 7786b55ec6..380ee418b4 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/DataAnnotationsMetadataProviderTest.cs @@ -8,6 +8,8 @@ using System.Linq; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal @@ -58,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal object expected) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var key = ModelMetadataIdentity.ForType(typeof(string)); var context = new DisplayMetadataProviderContext(key, new ModelAttributes(new object[] { attribute })); @@ -75,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateDisplayMetadata_FindsDisplayFormat_FromDataType() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var dataType = new DataTypeAttribute(DataType.Currency); var displayFormat = dataType.DisplayFormat; // Non-null for DataType.Currency. @@ -95,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateDisplayMetadata_FindsDisplayFormat_OverridingDataType() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var dataType = new DataTypeAttribute(DataType.Time); // Has a non-null DisplayFormat. var displayFormat = new DisplayFormatAttribute() // But these values override the values from DataType @@ -118,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateBindingMetadata_EditableAttributeFalse_SetsReadOnlyTrue() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var editable = new EditableAttribute(allowEdit: false); @@ -137,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateBindingMetadata_EditableAttributeTrue_SetsReadOnlyFalse() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var editable = new EditableAttribute(allowEdit: true); @@ -152,13 +154,47 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal Assert.False(context.BindingMetadata.IsReadOnly); } + // This is IMPORTANT. Product code needs to use GetName() instead of .Name. It's easy to regress. + [Fact] + public void CreateDisplayMetadata_DisplayAttribute_NameFromResources_NullLocalizer() + { + // Arrange + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); + + var display = new DisplayAttribute() + { +#if USE_REAL_RESOURCES + Name = nameof(Test.Resources.DisplayAttribute_Name), + ResourceType = typeof(Test.Resources), +#else + Name = nameof(DataAnnotations.Test.Resources.DisplayAttribute_Name), + ResourceType = typeof(TestResources), +#endif + }; + + var attributes = new Attribute[] { display }; + var key = ModelMetadataIdentity.ForType(typeof(string)); + var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes)); + + // Act + provider.CreateDisplayMetadata(context); + + // Assert + Assert.Equal("name from resources", context.DisplayMetadata.DisplayName()); + } // This is IMPORTANT. Product code needs to use GetName() instead of .Name. It's easy to regress. [Fact] - public void CreateDisplayMetadata_DisplayAttribute_NameFromResources() + public void CreateDisplayMetadata_DisplayAttribute_NameFromResources_WithLocalizer() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + // Nothing on stringLocalizer should be called + var stringLocalizer = new Mock(MockBehavior.Strict); + var stringLocalizerFactory = new Mock(); + stringLocalizerFactory + .Setup(s => s.Create(It.IsAny())) + .Returns(() => stringLocalizer.Object); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory.Object); var display = new DisplayAttribute() { @@ -184,10 +220,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal // This is IMPORTANT. Product code needs to use GetDescription() instead of .Description. It's easy to regress. [Fact] - public void CreateDisplayMetadata_DisplayAttribute_DescriptionFromResources() + public void CreateDisplayMetadata_DisplayAttribute_DescriptionFromResources_WithLocalizer() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + // Nothing on stringLocalizer should be called + var stringLocalizer = new Mock(MockBehavior.Strict); + var stringLocalizerFactory = new Mock(); + stringLocalizerFactory + .Setup(s => s.Create(It.IsAny())) + .Returns(() => stringLocalizer.Object); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory.Object); var display = new DisplayAttribute() { @@ -211,6 +253,141 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal Assert.Equal("description from resources", context.DisplayMetadata.Description()); } + // This is IMPORTANT. Product code needs to use GetDescription() instead of .Description. It's easy to regress. + [Fact] + public void CreateDisplayMetadata_DisplayAttribute_DescriptionFromResources_NullLocalizer() + { + // Arrange + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); + + var display = new DisplayAttribute() + { +#if USE_REAL_RESOURCES + Description = nameof(Test.Resources.DisplayAttribute_Description), + ResourceType = typeof(Test.Resources), +#else + Description = nameof(DataAnnotations.Test.Resources.DisplayAttribute_Description), + ResourceType = typeof(TestResources), +#endif + }; + + var attributes = new Attribute[] { display }; + var key = ModelMetadataIdentity.ForType(typeof(string)); + var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes)); + + // Act + provider.CreateDisplayMetadata(context); + + // Assert + Assert.Equal("description from resources", context.DisplayMetadata.Description()); + } + + // This is IMPORTANT. Product code needs to use GetPrompt() instead of .Prompt. It's easy to regress. + [Fact] + public void CreateDisplayMetadata_DisplayAttribute_PromptFromResources_WithLocalizer() + { + // Arrange + // Nothing on stringLocalizer should be called + var stringLocalizer = new Mock(MockBehavior.Strict); + var stringLocalizerFactory = new Mock(); + stringLocalizerFactory + .Setup(s => s.Create(It.IsAny())) + .Returns(() => stringLocalizer.Object); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory.Object); + + var display = new DisplayAttribute() + { +#if USE_REAL_RESOURCES + Prompt = nameof(Test.Resources.DisplayAttribute_Prompt), + ResourceType = typeof(Test.Resources), +#else + Prompt = nameof(DataAnnotations.Test.Resources.DisplayAttribute_Prompt), + ResourceType = typeof(TestResources), +#endif + }; + + var attributes = new Attribute[] { display }; + var key = ModelMetadataIdentity.ForType(typeof(string)); + var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes)); + + // Act + provider.CreateDisplayMetadata(context); + + // Assert + Assert.Equal("prompt from resources", context.DisplayMetadata.Placeholder()); + } + + // This is IMPORTANT. Product code needs to use GetPrompt() instead of .Prompt. It's easy to regress. + [Fact] + public void CreateDisplayMetadata_DisplayAttribute_PromptFromResources_NullLocalizer() + { + // Arrange + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); + + var display = new DisplayAttribute() + { +#if USE_REAL_RESOURCES + Prompt = nameof(Test.Resources.DisplayAttribute_Prompt), + ResourceType = typeof(Test.Resources), +#else + Prompt = nameof(DataAnnotations.Test.Resources.DisplayAttribute_Prompt), + ResourceType = typeof(TestResources), +#endif + }; + + var attributes = new Attribute[] { display }; + var key = ModelMetadataIdentity.ForType(typeof(string)); + var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes)); + + // Act + provider.CreateDisplayMetadata(context); + + // Assert + Assert.Equal("prompt from resources", context.DisplayMetadata.Placeholder()); + } + + [Fact] + public void CreateDisplayMetadata_DisplayAttribute_LocalizeProperties() + { + // Arrange + var stringLocalizer = new Mock(MockBehavior.Strict); + stringLocalizer + .Setup(s => s["Model_Name"]) + .Returns(new LocalizedString("Model_Name", "name from localizer")); + stringLocalizer + .Setup(s => s["Model_Description"]) + .Returns(new LocalizedString("Model_Description", "description from localizer")); + stringLocalizer + .Setup(s => s["Model_Prompt"]) + .Returns(new LocalizedString("Model_Prompt", "prompt from localizer")); + + var stringLocalizerFactory = new Mock(MockBehavior.Strict); + stringLocalizerFactory + .Setup(f => f.Create(It.IsAny())) + .Returns(stringLocalizer.Object); + + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory.Object); + + var display = new DisplayAttribute() + { + Name = "Model_Name", + Description = "Model_Description", + Prompt = "Model_Prompt" + }; + + var attributes = new Attribute[] { display }; + var key = ModelMetadataIdentity.ForType(typeof(DataAnnotationsMetadataProviderTest)); + var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes)); + + // Act + provider.CreateDisplayMetadata(context); + + // Assert + Assert.Equal("name from localizer", context.DisplayMetadata.DisplayName()); + Assert.Equal("description from localizer", context.DisplayMetadata.Description()); + Assert.Equal("prompt from localizer", context.DisplayMetadata.Placeholder()); + } + [Theory] [InlineData(typeof(EmptyClass), false)] [InlineData(typeof(ClassWithFields), false)] @@ -232,7 +409,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateDisplayMetadata_IsEnum_ReflectsModelType(Type type, bool expectedIsEnum) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; @@ -266,7 +443,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateDisplayMetadata_IsFlagsEnum_ReflectsModelType(Type type, bool expectedIsFlagsEnum) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; @@ -398,7 +575,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal IReadOnlyDictionary expectedDictionary) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; @@ -532,7 +709,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal IEnumerable> expectedKeyValuePairs) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var key = ModelMetadataIdentity.ForType(type); var attributes = new object[0]; @@ -554,7 +731,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateValidationMetadata_RequiredAttribute_SetsIsRequiredToTrue() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var required = new RequiredAttribute(); @@ -576,7 +753,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateValidationMetadata_NoRequiredAttribute_IsRequiredLeftAlone(bool? initialValue) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var attributes = new Attribute[] { }; var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); @@ -597,7 +774,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateBindingMetadata_RequiredAttribute_IsBindingRequiredLeftAlone(bool initialValue) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var attributes = new Attribute[] { new RequiredAttribute() }; var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); @@ -618,7 +795,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateBindingDetails_NoEditableAttribute_IsReadOnlyLeftAlone(bool? initialValue) { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var attributes = new Attribute[] { }; var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string)); @@ -636,7 +813,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateValidationDetails_ValidatableObject_ReturnsObject() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var attribute = new TestValidationAttribute(); var attributes = new Attribute[] { attribute }; @@ -655,7 +832,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal public void CreateValidationDetails_ValidatableObject_AlreadyInContext_Ignores() { // Arrange - var provider = new DataAnnotationsMetadataProvider(); + var provider = new DataAnnotationsMetadataProvider(stringLocalizerFactory: null); var attribute = new TestValidationAttribute(); var attributes = new Attribute[] { attribute }; diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs index ed343b3be0..c413d822e8 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/ModelMetadataProviderTest.cs @@ -1050,7 +1050,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal new DefaultCompositeMetadataDetailsProvider(new IMetadataDetailsProvider[] { new DefaultBindingMetadataProvider(), - new DataAnnotationsMetadataProvider(), + new DataAnnotationsMetadataProvider(stringLocalizerFactory: null), }), new TestOptionsManager()) { diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/TestResources.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/TestResources.cs index c5bc776640..9a6dcc3f85 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/TestResources.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Internal/TestResources.cs @@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding public static string DisplayAttribute_Name { get; } = Resources.DisplayAttribute_Name; + public static string DisplayAttribute_Prompt { get; } = Resources.DisplayAttribute_Prompt; + public static string DisplayAttribute_CultureSensitiveName => Resources.DisplayAttribute_Name + CultureInfo.CurrentUICulture; diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Properties/Resources.Designer.cs b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Properties/Resources.Designer.cs index 5863e76c9b..6424bee172 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Properties/Resources.Designer.cs +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Properties/Resources.Designer.cs @@ -58,6 +58,22 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Test return GetString("DisplayAttribute_Name"); } + /// + /// prompt from resources + /// + internal static string DisplayAttribute_Prompt + { + get { return GetString("DisplayAttribute_Prompt"); } + } + + /// + /// prompt from resources + /// + internal static string FormatDisplayAttribute_Prompt() + { + return GetString("DisplayAttribute_Prompt"); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Resources.resx b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Resources.resx index 9ddc5418f7..8ed00de591 100644 --- a/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Resources.resx +++ b/test/Microsoft.AspNetCore.Mvc.DataAnnotations.Test/Resources.resx @@ -126,4 +126,7 @@ name from resources + + prompt from resources + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs index a6b3f89719..53bbb14d54 100644 --- a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs +++ b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestModelMetadataProvider.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding { new DefaultBindingMetadataProvider(), new DefaultValidationMetadataProvider(), - new DataAnnotationsMetadataProvider(), + new DataAnnotationsMetadataProvider(stringLocalizerFactory: null), new DataMemberRequiredBindingMetadataProvider(), }; @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding { new DefaultBindingMetadataProvider(), new DefaultValidationMetadataProvider(), - new DataAnnotationsMetadataProvider(), + new DataAnnotationsMetadataProvider(stringLocalizerFactory: null), new DataMemberRequiredBindingMetadataProvider(), }; @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding { new DefaultBindingMetadataProvider(), new DefaultValidationMetadataProvider(), - new DataAnnotationsMetadataProvider(), + new DataAnnotationsMetadataProvider(stringLocalizerFactory: null), detailsProvider }), new TestOptionsManager())