diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs index 9dfdac729a..62dc4be9f4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs @@ -730,6 +730,22 @@ namespace Microsoft.AspNet.Mvc.Core return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_SelectExpressionNotEnumerable"), p0); } + /// + /// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}. + /// + internal static string HtmlHelper_TypeNotSupported_ForGetEnumSelectList + { + get { return GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"); } + } + + /// + /// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}. + /// + internal static string FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"), p0, p1, p2); + } + /// /// The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'. /// diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index 8de7be9913..2191b575b3 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -350,6 +350,39 @@ namespace Microsoft.AspNet.Mvc.Rendering additionalViewData); } + /// + public IEnumerable GetEnumSelectList() where TEnum : struct + { + var type = typeof(TEnum); + var metadata = MetadataProvider.GetMetadataForType(type); + if (!metadata.IsEnum || metadata.IsFlagsEnum) + { + var message = Resources.FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList( + type.FullName, + nameof(Enum).ToLowerInvariant(), + nameof(FlagsAttribute)); + throw new ArgumentException(message, nameof(TEnum)); + } + + return GetEnumSelectList(metadata); + } + + /// + public IEnumerable GetEnumSelectList([NotNull] Type enumType) + { + var metadata = MetadataProvider.GetMetadataForType(enumType); + if (!metadata.IsEnum || metadata.IsFlagsEnum) + { + var message = Resources.FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList( + enumType.FullName, + nameof(Enum).ToLowerInvariant(), + nameof(FlagsAttribute)); + throw new ArgumentException(message, nameof(enumType)); + } + + return GetEnumSelectList(metadata); + } + /// public HtmlString Hidden(string expression, object value, object htmlAttributes) { @@ -1016,5 +1049,43 @@ namespace Microsoft.AspNet.Mvc.Rendering { return _htmlGenerator.GetClientValidationRules(ViewContext, modelExplorer, expression); } + + /// + /// Returns a select list for the given . + /// + /// to generate a select list for. + /// + /// An containing the select list for the given + /// . + /// + /// + /// Thrown if 's is not an + /// or if it has a . + /// + protected virtual IEnumerable GetEnumSelectList([NotNull] ModelMetadata metadata) + { + if (!metadata.IsEnum || metadata.IsFlagsEnum) + { + var message = Resources.FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList( + metadata.ModelType.FullName, + nameof(Enum).ToLowerInvariant(), + nameof(FlagsAttribute)); + throw new ArgumentException(message, nameof(metadata)); + } + + var selectList = new List(); + foreach (var keyValuePair in metadata.EnumDisplayNamesAndValues) + { + var selectListItem = new SelectListItem + { + Text = keyValuePair.Key, + Value = keyValuePair.Value, + }; + + selectList.Add(selectListItem); + } + + return selectList; + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs index c88702865a..9b5931aa20 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.ModelBinding; @@ -374,7 +375,37 @@ namespace Microsoft.AspNet.Mvc.Rendering /// is null; ignored otherwise. /// /// An containing the relevant rules. - IEnumerable GetClientValidationRules(ModelExplorer modelExplorer, string expression); + IEnumerable GetClientValidationRules( + ModelExplorer modelExplorer, + string expression); + + /// + /// Returns a select list for the given . + /// + /// Type to generate a select list for. + /// + /// An containing the select list for the given + /// . + /// + /// + /// Thrown if is not an or if it has a + /// . + /// + IEnumerable GetEnumSelectList() where TEnum : struct; + + /// + /// Returns a select list for the given . + /// + /// to generate a select list for. + /// + /// An containing the select list for the given + /// . + /// + /// + /// Thrown if is not an or if it has a + /// . + /// + IEnumerable GetEnumSelectList([NotNull] Type enumType); /// /// Returns an <input> element of type "hidden" for the specified . diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx index 20099dea90..2ad230d2c9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx @@ -253,6 +253,9 @@ The parameter '{0}' must evaluate to an IEnumerable when multiple selection is allowed. + + The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}. + The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'. diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Properties/Resources.Designer.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..300525e63d --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Properties/Resources.Designer.cs @@ -0,0 +1,46 @@ +// +namespace Microsoft.AspNet.Mvc.Core.Test +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNet.Mvc.Core.Test.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// name from resources + /// + internal static string DisplayAttribute_Name + { + get { return GetString("DisplayAttribute_Name"); } + } + + /// + /// name from resources + /// + internal static string FormatDisplayAttribute_Name() + { + return GetString("DisplayAttribute_Name"); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs index 467d9ab9c5..7c27d4cfea 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/DefaultEditorTemplatesTest.cs @@ -1012,12 +1012,22 @@ Environment.NewLine; } public IEnumerable GetClientValidationRules( - ModelExplorer modelExplorer, + ModelExplorer modelExplorer, string name) { return Enumerable.Empty(); } + public IEnumerable GetEnumSelectList() where TEnum : struct + { + throw new NotImplementedException(); + } + + public IEnumerable GetEnumSelectList([NotNull] Type enumType) + { + throw new NotImplementedException(); + } + public HtmlString Hidden(string name, object value, object htmlAttributes) { return new HtmlString("__Hidden__"); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperSelectTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperSelectTest.cs index 525a290db0..cf972b1adf 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperSelectTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/HtmlHelperSelectTest.cs @@ -3,7 +3,13 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Testing; +using Microsoft.Framework.Internal; +using Microsoft.Framework.WebEncoders; +using Moq; using Xunit; namespace Microsoft.AspNet.Mvc.Rendering @@ -858,6 +864,354 @@ namespace Microsoft.AspNet.Mvc.Rendering Assert.Equal(savedSelected, selectList.Select(item => item.Selected)); } + [Fact] + public void GetEnumSelectListTEnum_ThrowsWithFlagsEnum() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => htmlHelper.GetEnumSelectList(), + "TEnum", + $"The type '{ typeof(EnumWithFlags).FullName }' is not supported."); + } + + [Fact] + public void GetEnumSelectListTEnum_ThrowsWithNonEnum() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => htmlHelper.GetEnumSelectList(), + "TEnum", + $"The type '{ typeof(StructWithFields).FullName }' is not supported."); + } + + [Fact] + public void GetEnumSelectListTEnum_WrapsGetEnumSelectListModelMetadata() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var metadata = metadataProvider.GetMetadataForType(typeof(EnumWithFields)); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act + var result = htmlHelper.GetEnumSelectList(); + + // Assert + Assert.Equal(metadata.ModelType, htmlHelper.Metadata.ModelType); + + Assert.Same(htmlHelper.SelectListItems, result); // No replacement of the underlying List + VerifySelectList(htmlHelper.CopiedSelectListItems, result); // No change to the (mutable) items + } + + [Fact] + public void GetEnumSelectListType_ThrowsWithFlagsEnum() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => htmlHelper.GetEnumSelectList(typeof(EnumWithFlags)), + "enumType", + $"The type '{ typeof(EnumWithFlags).FullName }' is not supported."); + } + + [Fact] + public void GetEnumSelectListType_ThrowsWithNonEnum() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => htmlHelper.GetEnumSelectList(typeof(StructWithFields)), + "enumType", + $"The type '{ typeof(StructWithFields).FullName }' is not supported."); + } + + [Fact] + public void GetEnumSelectListType_ThrowsWithNonStruct() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act & Assert + ExceptionAssert.ThrowsArgument( + () => htmlHelper.GetEnumSelectList(typeof(ClassWithFields)), + "enumType", + $"The type '{ typeof(ClassWithFields).FullName }' is not supported."); + } + + [Fact] + public void GetEnumSelectListType_WrapsGetEnumSelectListModelMetadata() + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var metadata = metadataProvider.GetMetadataForType(typeof(EnumWithFields)); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act + var result = htmlHelper.GetEnumSelectList(typeof(EnumWithFields)); + + // Assert + Assert.Equal(metadata.ModelType, htmlHelper.Metadata.ModelType); + + Assert.Same(htmlHelper.SelectListItems, result); // No replacement of the underlying List + VerifySelectList(htmlHelper.CopiedSelectListItems, result); // No change to the (mutable) items + } + + public static TheoryData> GetEnumSelectListData + { + get + { + return new TheoryData> + { + { typeof(EmptyEnum), Enumerable.Empty() }, + { typeof(EmptyEnum?), Enumerable.Empty() }, + { + typeof(EnumWithDisplayNames), + new List + { + new SelectListItem { Text = "cero", Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDisplayNames.One), Value = "1" }, + new SelectListItem { Text = "dos", Value = "2" }, + new SelectListItem { Text = "tres", Value = "3" }, + new SelectListItem { Text = "name from resources", Value = "-2" }, + new SelectListItem { Text = "menos uno", Value = "-1" }, + } + }, + { + typeof(EnumWithDisplayNames?), + new List + { + new SelectListItem { Text = "cero", Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDisplayNames.One), Value = "1" }, + new SelectListItem { Text = "dos", Value = "2" }, + new SelectListItem { Text = "tres", Value = "3" }, + new SelectListItem { Text = "name from resources", Value = "-2" }, + new SelectListItem { Text = "menos uno", Value = "-1" }, + } + }, + { + typeof(EnumWithDuplicates), + new List + { + new SelectListItem { Text = nameof(EnumWithDuplicates.Zero), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.None), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.One), Value = "1" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Duece), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Two), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.MoreThanTwo), Value = "3" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Three), Value = "3" }, + } + }, + { + typeof(EnumWithDuplicates?), + new List + { + new SelectListItem { Text = nameof(EnumWithDuplicates.Zero), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.None), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.One), Value = "1" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Duece), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Two), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.MoreThanTwo), Value = "3" }, + new SelectListItem { Text = nameof(EnumWithDuplicates.Three), Value = "3" }, + } + }, + { + typeof(EnumWithFields), + new List + { + new SelectListItem { Text = nameof(EnumWithFields.Zero), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithFields.One), Value = "1" }, + new SelectListItem { Text = nameof(EnumWithFields.Two), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithFields.Three), Value = "3" }, + new SelectListItem { Text = nameof(EnumWithFields.MinusTwo), Value = "-2" }, + new SelectListItem { Text = nameof(EnumWithFields.MinusOne), Value = "-1" }, + } + }, + { + typeof(EnumWithFields?), + new List + { + new SelectListItem { Text = nameof(EnumWithFields.Zero), Value = "0" }, + new SelectListItem { Text = nameof(EnumWithFields.One), Value = "1" }, + new SelectListItem { Text = nameof(EnumWithFields.Two), Value = "2" }, + new SelectListItem { Text = nameof(EnumWithFields.Three), Value = "3" }, + new SelectListItem { Text = nameof(EnumWithFields.MinusTwo), Value = "-2" }, + new SelectListItem { Text = nameof(EnumWithFields.MinusOne), Value = "-1" }, + } + }, + }; + } + } + + [Theory] + [MemberData(nameof(GetEnumSelectListData))] + public void GetEnumSelectList_ReturnsExpectedItems(Type type, IEnumerable expected) + { + // Arrange + var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); + var metadata = metadataProvider.GetMetadataForType(type); + var htmlHelper = new TestHtmlHelper(metadataProvider); + + // Act + var result = htmlHelper.GetEnumSelectList(type); + + // Assert + VerifySelectList(expected, result); + } + + // Confirm methods that wrap GetEnumSelectList(ModelMetadata) are not changing anything in returned collection. + private void VerifySelectList(IEnumerable expected, IEnumerable actual) + { + Assert.NotNull(actual); + Assert.Equal(expected.Count(), actual.Count()); + for (var i = 0; i < actual.Count(); i++) + { + var expectedItem = expected.ElementAt(i); + var actualItem = actual.ElementAt(i); + + Assert.False(actualItem.Disabled); + Assert.Null(actualItem.Group); + Assert.False(actualItem.Selected); + Assert.Equal(expectedItem.Text, actualItem.Text); + Assert.Equal(expectedItem.Value, actualItem.Value); + } + } + + private class TestHtmlHelper : HtmlHelper + { + public TestHtmlHelper([NotNull] IModelMetadataProvider metadataProvider) + : base( + new Mock(MockBehavior.Strict).Object, + new Mock(MockBehavior.Strict).Object, + metadataProvider, + new Mock(MockBehavior.Strict).Object, + new Mock(MockBehavior.Strict).Object, + new Mock(MockBehavior.Strict).Object) + { + } + + public ModelMetadata Metadata { get; private set; } + + public IEnumerable SelectListItems { get; private set; } + + public IEnumerable CopiedSelectListItems { get; private set; } + + protected override IEnumerable GetEnumSelectList([NotNull] ModelMetadata metadata) + { + Metadata = metadata; + SelectListItems = base.GetEnumSelectList(metadata); + if (SelectListItems != null) + { + // Perform a deep copy to help confirm the mutable items are not changed. + var copiedSelectListItems = new List(); + CopiedSelectListItems = copiedSelectListItems; + foreach (var item in SelectListItems) + { + var copy = new SelectListItem + { + Disabled = item.Disabled, + Group = item.Group, + Selected = item.Selected, + Text = item.Text, + Value = item.Value, + }; + + copiedSelectListItems.Add(copy); + } + } + + return SelectListItems; + } + } + + private class ClassWithFields + { + public const int Zero = 0; + + public const int One = 1; + } + + private enum EmptyEnum + { + } + + private enum EnumWithDisplayNames + { + [Display(Name = "tres")] + Three = 3, + + [Display(Name = "dos")] + Two = 2, + + // Display attribute exists but does not set Name. + [Display(ShortName = "uno")] + One = 1, + + [Display(Name = "cero")] + Zero = 0, + + [Display(Name = "menos uno")] + MinusOne = -1, + +#if USE_REAL_RESOURCES + [Display(Name = nameof(Test.Resources.DisplayAttribute_Name), ResourceType = typeof(Test.Resources))] +#else + [Display(Name = nameof(TestResources.DisplayAttribute_Name), ResourceType = typeof(TestResources))] +#endif + MinusTwo = -2, + } + + private enum EnumWithDuplicates + { + Zero = 0, + One = 1, + Three = 3, + MoreThanTwo = 3, + Two = 2, + None = 0, + Duece = 2, + } + + [Flags] + private enum EnumWithFlags + { + Four = 4, + Two = 2, + One = 1, + Zero = 0, + All = -1, + } + + private enum EnumWithFields + { + MinusTwo = -2, + MinusOne = -1, + Three = 3, + Two = 2, + One = 1, + Zero = 0, + } + + private struct StructWithFields + { + public const int Zero = 0; + + public const int One = 1; + } + private class ModelContainingList { public List Property1 { get; } = new List(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/TestResources.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/TestResources.cs new file mode 100644 index 0000000000..1d66d7f313 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Rendering/TestResources.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Mvc.Core.Test; + +namespace Microsoft.AspNet.Mvc +{ + // Wrap resources to make them available as public properties for [Display]. That attribute does not support + // internal properties. + public class TestResources + { + public static string DisplayAttribute_Name { get; } = Resources.DisplayAttribute_Name; + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Resources.resx b/test/Microsoft.AspNet.Mvc.Core.Test/Resources.resx new file mode 100644 index 0000000000..08344b52fb --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Resources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + name from resources + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs index b2353ee428..0b50d8ed40 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs +++ b/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Metadata/DataAnnotationsMetadataProviderTest.cs @@ -422,7 +422,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata new List> { new KeyValuePair("cero", "0"), - new KeyValuePair("uno", "1"), + new KeyValuePair(nameof(EnumWithDisplayNames.One), "1"), new KeyValuePair("dos", "2"), new KeyValuePair("tres", "3"), new KeyValuePair("name from resources", "-2"), @@ -434,7 +434,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata new List> { new KeyValuePair("cero", "0"), - new KeyValuePair("uno", "1"), + new KeyValuePair(nameof(EnumWithDisplayNames.One), "1"), new KeyValuePair("dos", "2"), new KeyValuePair("tres", "3"), new KeyValuePair("name from resources", "-2"), @@ -569,7 +569,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata [Display(Name = "dos")] Two = 2, - [Display(Name = "uno")] + // Display attribute exists but does not set Name. + [Display(ShortName = "uno")] One = 1, [Display(Name = "cero")]