Add `Html.GetEnumSelectList()` helpers

- #438 part 2/2

nit: test `[Display]` that does not set `Name` in `DataAnnotationsMetadataProviderTest`
This commit is contained in:
Doug Bunting 2015-03-22 12:40:44 -07:00
parent 90773ab925
commit 570b1e583a
10 changed files with 674 additions and 5 deletions

View File

@ -730,6 +730,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_SelectExpressionNotEnumerable"), p0);
}
/// <summary>
/// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}.
/// </summary>
internal static string HtmlHelper_TypeNotSupported_ForGetEnumSelectList
{
get { return GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"); }
}
/// <summary>
/// The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}.
/// </summary>
internal static string FormatHtmlHelper_TypeNotSupported_ForGetEnumSelectList(object p0, object p1, object p2)
{
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlHelper_TypeNotSupported_ForGetEnumSelectList"), p0, p1, p2);
}
/// <summary>
/// The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.
/// </summary>

View File

@ -350,6 +350,39 @@ namespace Microsoft.AspNet.Mvc.Rendering
additionalViewData);
}
/// <inheritdoc />
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() 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);
}
/// <inheritdoc />
public IEnumerable<SelectListItem> 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);
}
/// <inheritdoc />
public HtmlString Hidden(string expression, object value, object htmlAttributes)
{
@ -1016,5 +1049,43 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
return _htmlGenerator.GetClientValidationRules(ViewContext, modelExplorer, expression);
}
/// <summary>
/// Returns a select list for the given <paramref name="metadata"/>.
/// </summary>
/// <param name="metadata"><see cref="ModelMetadata"/> to generate a select list for.</param>
/// <returns>
/// An <see cref="IEnumerable{SelectListItem}"/> containing the select list for the given
/// <paramref name="metadata"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="metadata"/>'s <see cref="ModelMetadata.ModelType"/> is not an <see cref="Enum"/>
/// or if it has a <see cref="FlagsAttribute"/>.
/// </exception>
protected virtual IEnumerable<SelectListItem> 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<SelectListItem>();
foreach (var keyValuePair in metadata.EnumDisplayNamesAndValues)
{
var selectListItem = new SelectListItem
{
Text = keyValuePair.Key,
Value = keyValuePair.Value,
};
selectList.Add(selectListItem);
}
return selectList;
}
}
}

View File

@ -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
/// <paramref name="metadata"/> is <c>null</c>; ignored otherwise.
/// </param>
/// <returns>An <see cref="IEnumerable{ModelClientValidationRule}"/> containing the relevant rules.</returns>
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelExplorer modelExplorer, string expression);
IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelExplorer modelExplorer,
string expression);
/// <summary>
/// Returns a select list for the given <typeparamref name="TEnum"/>.
/// </summary>
/// <typeparam name="TEnum">Type to generate a select list for.</typeparam>
/// <returns>
/// An <see cref="IEnumerable{SelectListItem}"/> containing the select list for the given
/// <typeparamref name="TEnum"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown if <typeparamref name="TEnum"/> is not an <see cref="Enum"/> or if it has a
/// <see cref="FlagsAttribute"/>.
/// </exception>
IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct;
/// <summary>
/// Returns a select list for the given <paramref name="enumType"/>.
/// </summary>
/// <param name="enumType"><see cref="Type"/> to generate a select list for.</param>
/// <returns>
/// An <see cref="IEnumerable{SelectListItem}"/> containing the select list for the given
/// <paramref name="enumType"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="enumType"/> is not an <see cref="Enum"/> or if it has a
/// <see cref="FlagsAttribute"/>.
/// </exception>
IEnumerable<SelectListItem> GetEnumSelectList([NotNull] Type enumType);
/// <summary>
/// Returns an &lt;input&gt; element of type "hidden" for the specified <paramref name="expression"/>.

View File

@ -253,6 +253,9 @@
<data name="HtmlHelper_SelectExpressionNotEnumerable" xml:space="preserve">
<value>The parameter '{0}' must evaluate to an IEnumerable when multiple selection is allowed.</value>
</data>
<data name="HtmlHelper_TypeNotSupported_ForGetEnumSelectList" xml:space="preserve">
<value>The type '{0}' is not supported. Type must be an {1} that does not have an associated {2}.</value>
</data>
<data name="HtmlHelper_WrongSelectDataType" xml:space="preserve">
<value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value>
</data>

View File

@ -0,0 +1,46 @@
// <auto-generated />
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);
/// <summary>
/// name from resources
/// </summary>
internal static string DisplayAttribute_Name
{
get { return GetString("DisplayAttribute_Name"); }
}
/// <summary>
/// name from resources
/// </summary>
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;
}
}
}

View File

@ -1012,12 +1012,22 @@ Environment.NewLine;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelExplorer modelExplorer,
ModelExplorer modelExplorer,
string name)
{
return Enumerable.Empty<ModelClientValidationRule>();
}
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct
{
throw new NotImplementedException();
}
public IEnumerable<SelectListItem> GetEnumSelectList([NotNull] Type enumType)
{
throw new NotImplementedException();
}
public HtmlString Hidden(string name, object value, object htmlAttributes)
{
return new HtmlString("__Hidden__");

View File

@ -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<EnumWithFlags>(),
"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<StructWithFields>(),
"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<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
}
[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<Type, IEnumerable<SelectListItem>> GetEnumSelectListData
{
get
{
return new TheoryData<Type, IEnumerable<SelectListItem>>
{
{ typeof(EmptyEnum), Enumerable.Empty<SelectListItem>() },
{ typeof(EmptyEnum?), Enumerable.Empty<SelectListItem>() },
{
typeof(EnumWithDisplayNames),
new List<SelectListItem>
{
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<SelectListItem>
{
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<SelectListItem>
{
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<SelectListItem>
{
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<SelectListItem>
{
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<SelectListItem>
{
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<SelectListItem> 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<SelectListItem> expected, IEnumerable<SelectListItem> 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<IHtmlGenerator>(MockBehavior.Strict).Object,
new Mock<ICompositeViewEngine>(MockBehavior.Strict).Object,
metadataProvider,
new Mock<IHtmlEncoder>(MockBehavior.Strict).Object,
new Mock<IUrlEncoder>(MockBehavior.Strict).Object,
new Mock<IJavaScriptStringEncoder>(MockBehavior.Strict).Object)
{
}
public ModelMetadata Metadata { get; private set; }
public IEnumerable<SelectListItem> SelectListItems { get; private set; }
public IEnumerable<SelectListItem> CopiedSelectListItems { get; private set; }
protected override IEnumerable<SelectListItem> 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<SelectListItem>();
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<string> Property1 { get; } = new List<string>();

View File

@ -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;
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DisplayAttribute_Name" xml:space="preserve">
<value>name from resources</value>
</data>
</root>

View File

@ -422,7 +422,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("cero", "0"),
new KeyValuePair<string, string>("uno", "1"),
new KeyValuePair<string, string>(nameof(EnumWithDisplayNames.One), "1"),
new KeyValuePair<string, string>("dos", "2"),
new KeyValuePair<string, string>("tres", "3"),
new KeyValuePair<string, string>("name from resources", "-2"),
@ -434,7 +434,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("cero", "0"),
new KeyValuePair<string, string>("uno", "1"),
new KeyValuePair<string, string>(nameof(EnumWithDisplayNames.One), "1"),
new KeyValuePair<string, string>("dos", "2"),
new KeyValuePair<string, string>("tres", "3"),
new KeyValuePair<string, string>("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")]