Add `enum`-related details to `ModelMetadata`
- #438 part 1/2 and #2027 part 1/3 slight oddity in the XML docs - unfortunately Roslyn seems to ignore `TypeInfo` being a subclass of `Type` nit: use correct `warningsAsErrors` datatype in test project.json
This commit is contained in:
parent
c62974d39b
commit
7dd3afe3d1
|
|
@ -1,8 +1,11 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
||||
|
|
@ -91,6 +94,39 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
displayMetadata.EditFormatString = displayFormatAttribute.DataFormatString;
|
||||
}
|
||||
|
||||
// IsEnum et cetera
|
||||
var underlyingType = Nullable.GetUnderlyingType(context.Key.ModelType) ?? context.Key.ModelType;
|
||||
if (underlyingType.IsEnum())
|
||||
{
|
||||
// IsEnum
|
||||
displayMetadata.IsEnum = true;
|
||||
|
||||
// IsFlagsEnum
|
||||
var underlyingTypeInfo = underlyingType.GetTypeInfo();
|
||||
displayMetadata.IsFlagsEnum =
|
||||
underlyingTypeInfo.GetCustomAttribute<FlagsAttribute>(inherit: false) != null;
|
||||
|
||||
// EnumDisplayNamesAndValues and EnumNamesAndValues
|
||||
//
|
||||
// Order EnumDisplayNamesAndValues to match Enum.GetNames(). That method orders by absolute value,
|
||||
// then its behavior is undefined (but hopefully stable). Add to EnumNamesAndValues in same order but
|
||||
// Dictionary does not guarantee order will be preserved.
|
||||
var displayNamesAndValues = new List<KeyValuePair<string, string>>();
|
||||
var namesAndValues = new Dictionary<string, string>();
|
||||
foreach (var name in Enum.GetNames(underlyingType))
|
||||
{
|
||||
var field = underlyingType.GetField(name);
|
||||
var displayName = GetDisplayName(field);
|
||||
var value = ((Enum)field.GetValue(obj: null)).ToString("d");
|
||||
|
||||
displayNamesAndValues.Add(new KeyValuePair<string, string>(displayName, value));
|
||||
namesAndValues.Add(name, value);
|
||||
}
|
||||
|
||||
displayMetadata.EnumDisplayNamesAndValues = displayNamesAndValues;
|
||||
displayMetadata.EnumNamesAndValues = namesAndValues;
|
||||
}
|
||||
|
||||
// HasNonDefaultEditFormat
|
||||
if (!string.IsNullOrEmpty(displayFormatAttribute?.DataFormatString) &&
|
||||
displayFormatAttribute?.ApplyFormatInEditMode == true)
|
||||
|
|
@ -139,7 +175,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
displayMetadata.Order = displayAttribute.GetOrder().Value;
|
||||
}
|
||||
|
||||
// ShowForDisplay
|
||||
// ShowForDisplay
|
||||
if (scaffoldColumnAttribute != null)
|
||||
{
|
||||
displayMetadata.ShowForDisplay = scaffoldColumnAttribute.Scaffold;
|
||||
|
|
@ -157,7 +193,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
displayMetadata.SimpleDisplayProperty = displayColumnAttribute.DisplayColumn;
|
||||
}
|
||||
|
||||
// TemplateHinte
|
||||
// TemplateHint
|
||||
if (uiHintAttribute != null)
|
||||
{
|
||||
displayMetadata.TemplateHint = uiHintAttribute.UIHint;
|
||||
|
|
@ -176,5 +212,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
context.ValidationMetadata.ValiatorMetadata.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// Return non-empty name specified in a [Display] attribute for a field, if any; field.Name otherwise.
|
||||
private static string GetDisplayName(FieldInfo field)
|
||||
{
|
||||
var display = field.GetCustomAttribute<DisplayAttribute>(inherit: false);
|
||||
if (display != null)
|
||||
{
|
||||
var name = display.GetName();
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return field.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,6 +210,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<KeyValuePair<string, string>> EnumDisplayNamesAndValues
|
||||
{
|
||||
get
|
||||
{
|
||||
return DisplayMetadata.EnumDisplayNamesAndValues;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IReadOnlyDictionary<string, string> EnumNamesAndValues
|
||||
{
|
||||
get
|
||||
{
|
||||
return DisplayMetadata.EnumNamesAndValues;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasNonDefaultEditFormat
|
||||
{
|
||||
|
|
@ -237,6 +255,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsEnum
|
||||
{
|
||||
get
|
||||
{
|
||||
return DisplayMetadata.IsEnum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsFlagsEnum
|
||||
{
|
||||
get
|
||||
{
|
||||
return DisplayMetadata.IsFlagsEnum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
|
|
@ -274,7 +310,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
_isRequired = !ModelType.AllowsNullValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _isRequired.Value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// </remarks>
|
||||
public string EditFormatString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ordered display names and values of all <see cref="System.Enum"/> values in
|
||||
/// <see cref="ModelMetadata.ModelType"/> or <c>Nullable.GetUnderlyingType(ModelType)</c>. See
|
||||
/// <see cref="ModelMetadata.EnumDisplayNamesAndValues"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<KeyValuePair<string, string>> EnumDisplayNamesAndValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names and values of all <see cref="System.Enum"/> values in <see cref="ModelMetadata.ModelType"/>
|
||||
/// or <c>Nullable.GetUnderlyingType(ModelType)</c>. See <see cref="ModelMetadata.EnumNamesAndValues"/>.
|
||||
/// </summary>
|
||||
// This could be implemented in DefaultModelMetadata. But value should be cached.
|
||||
public IReadOnlyDictionary<string, string> EnumNamesAndValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the model has a non-default edit format.
|
||||
/// See <see cref="ModelMetadata.HasNonDefaultEditFormat"/>
|
||||
|
|
@ -73,6 +87,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
/// </summary>
|
||||
public bool HtmlEncode { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="ModelMetadata.ModelType"/> or
|
||||
/// <c>Nullable.GetUnderlyingType(ModelType)</c> is for an <see cref="System.Enum"/>. See
|
||||
/// <see cref="ModelMetadata.IsEnum"/>.
|
||||
/// </summary>
|
||||
// This could be implemented in DefaultModelMetadata. But value is needed in the details provider.
|
||||
public bool IsEnum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="ModelMetadata.ModelType"/> or
|
||||
/// <c>Nullable.GetUnderlyingType(ModelType)</c> is for an <see cref="System.Enum"/> with an associated
|
||||
/// <see cref="System.FlagsAttribute"/>. See <see cref="ModelMetadata.IsFlagsEnum"/>.
|
||||
/// </summary>
|
||||
// This could be implemented in DefaultModelMetadata. But value is needed in the details provider.
|
||||
public bool IsFlagsEnum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text to display when the model value is null.
|
||||
/// See <see cref="ModelMetadata.NullDisplayText"/>
|
||||
|
|
|
|||
|
|
@ -120,6 +120,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public abstract string EditFormatString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ordered display names and values of all <see cref="Enum"/> values in <see cref="ModelType"/> or
|
||||
/// <c>Nullable.GetUnderlyingType(ModelType)</c>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An <see cref="IEnumerable{KeyValuePair{string, string}}"/> of mappings between <see cref="Enum"/> field names
|
||||
/// and values. <c>null</c> if <see cref="IsEnum"/> is <c>false</c>.
|
||||
/// </value>
|
||||
public abstract IEnumerable<KeyValuePair<string, string>> EnumDisplayNamesAndValues { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names and values of all <see cref="Enum"/> values in <see cref="ModelType"/> or
|
||||
/// <c>Nullable.GetUnderlyingType(ModelType)</c>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// An <see cref="IReadOnlyDictionary{string, string}"/> of mappings between <see cref="Enum"/> field names
|
||||
/// and values. <c>null</c> if <see cref="IsEnum"/> is <c>false</c>.
|
||||
/// </value>
|
||||
public abstract IReadOnlyDictionary<string, string> EnumNamesAndValues { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="EditFormatString"/> has a non-<c>null</c>, non-empty
|
||||
/// value different from the default for the datatype.
|
||||
|
|
@ -145,6 +165,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </remarks>
|
||||
public abstract bool HideSurroundingHtml { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="ModelType"/> or <c>Nullable.GetUnderlyingType(ModelType)</c> is
|
||||
/// for an <see cref="Enum"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if <c>type.IsEnum</c> (<c>type.GetTypeInfo().IsEnum</c> for DNX Core 5.0) is <c>true</c> for
|
||||
/// <see cref="ModelType"/> or <c>Nullable.GetUnderlyingType(ModelType)</c>; <c>false</c> otherwise.
|
||||
/// </value>
|
||||
public abstract bool IsEnum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="ModelType"/> or <c>Nullable.GetUnderlyingType(ModelType)</c> is
|
||||
/// for an <see cref="Enum"/> with an associated <see cref="FlagsAttribute"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if <see cref="IsEnum"/> is <c>true</c> and <see cref="ModelType"/> or
|
||||
/// <c>Nullable.GetUnderlyingType(ModelType)</c> has an associated <see cref="FlagsAttribute"/>; <c>false</c>
|
||||
/// otherwise.
|
||||
/// </value>
|
||||
public abstract bool IsFlagsEnum { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not the model value is read-only. This is only applicable when
|
||||
/// the current instance represents a property.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
{
|
||||
public class DataAnnotationsMetadataProviderTest
|
||||
{
|
||||
// Includes attributes with a 'simple' effect on display details.
|
||||
// Includes attributes with a 'simple' effect on display details.
|
||||
public static TheoryData<object, Func<DisplayMetadata, object>, object> DisplayDetailsData
|
||||
{
|
||||
get
|
||||
|
|
@ -47,8 +48,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
[Theory]
|
||||
[MemberData(nameof(DisplayDetailsData))]
|
||||
public void GetDisplayDetails_SimpleAttributes(
|
||||
object attribute,
|
||||
Func<DisplayMetadata, object> accessor,
|
||||
object attribute,
|
||||
Func<DisplayMetadata, object> accessor,
|
||||
object expected)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -193,5 +194,437 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
// Assert
|
||||
Assert.Equal("description from resources", context.DisplayMetadata.Description);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(EmptyClass), false)]
|
||||
[InlineData(typeof(ClassWithFields), false)]
|
||||
[InlineData(typeof(ClassWithProperties), false)]
|
||||
[InlineData(typeof(EmptyEnum), true)]
|
||||
[InlineData(typeof(EmptyEnum?), true)]
|
||||
[InlineData(typeof(EnumWithDisplayNames), true)]
|
||||
[InlineData(typeof(EnumWithDisplayNames?), true)]
|
||||
[InlineData(typeof(EnumWithDuplicates), true)]
|
||||
[InlineData(typeof(EnumWithDuplicates?), true)]
|
||||
[InlineData(typeof(EnumWithFlags), true)]
|
||||
[InlineData(typeof(EnumWithFlags?), true)]
|
||||
[InlineData(typeof(EnumWithFields), true)]
|
||||
[InlineData(typeof(EnumWithFields?), true)]
|
||||
[InlineData(typeof(EmptyStruct), false)]
|
||||
[InlineData(typeof(StructWithFields), false)]
|
||||
[InlineData(typeof(StructWithFields?), false)]
|
||||
[InlineData(typeof(StructWithProperties), false)]
|
||||
public void GetDisplayDetails_IsEnum_ReflectsModelType(Type type, bool expectedIsEnum)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedIsEnum, context.DisplayMetadata.IsEnum);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(EmptyClass), false)]
|
||||
[InlineData(typeof(ClassWithFields), false)]
|
||||
[InlineData(typeof(ClassWithProperties), false)]
|
||||
[InlineData(typeof(EmptyEnum), false)]
|
||||
[InlineData(typeof(EmptyEnum?), false)]
|
||||
[InlineData(typeof(EnumWithDisplayNames), false)]
|
||||
[InlineData(typeof(EnumWithDisplayNames?), false)]
|
||||
[InlineData(typeof(EnumWithDuplicates), false)]
|
||||
[InlineData(typeof(EnumWithDuplicates?), false)]
|
||||
[InlineData(typeof(EnumWithFlags), true)]
|
||||
[InlineData(typeof(EnumWithFlags?), true)]
|
||||
[InlineData(typeof(EnumWithFields), false)]
|
||||
[InlineData(typeof(EnumWithFields?), false)]
|
||||
[InlineData(typeof(EmptyStruct), false)]
|
||||
[InlineData(typeof(StructWithFields), false)]
|
||||
[InlineData(typeof(StructWithFields?), false)]
|
||||
[InlineData(typeof(StructWithProperties), false)]
|
||||
public void GetDisplayDetails_IsFlagsEnum_ReflectsModelType(Type type, bool expectedIsFlagsEnum)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedIsFlagsEnum, context.DisplayMetadata.IsFlagsEnum);
|
||||
}
|
||||
|
||||
// Type -> expected EnumNamesAndValues
|
||||
public static TheoryData<Type, IReadOnlyDictionary<string, string>> EnumNamesData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<Type, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
{ typeof(ClassWithFields), null },
|
||||
{ typeof(StructWithFields), null },
|
||||
{ typeof(StructWithFields?), null },
|
||||
{ typeof(EmptyEnum), new Dictionary<string, string>() },
|
||||
{ typeof(EmptyEnum?), new Dictionary<string, string>() },
|
||||
{
|
||||
typeof(EnumWithDisplayNames),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithDisplayNames.MinusTwo), "-2" },
|
||||
{ nameof(EnumWithDisplayNames.MinusOne), "-1" },
|
||||
{ nameof(EnumWithDisplayNames.Zero), "0" },
|
||||
{ nameof(EnumWithDisplayNames.One), "1" },
|
||||
{ nameof(EnumWithDisplayNames.Two), "2" },
|
||||
{ nameof(EnumWithDisplayNames.Three), "3" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithDisplayNames?),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithDisplayNames.MinusTwo), "-2" },
|
||||
{ nameof(EnumWithDisplayNames.MinusOne), "-1" },
|
||||
{ nameof(EnumWithDisplayNames.Zero), "0" },
|
||||
{ nameof(EnumWithDisplayNames.One), "1" },
|
||||
{ nameof(EnumWithDisplayNames.Two), "2" },
|
||||
{ nameof(EnumWithDisplayNames.Three), "3" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithDuplicates),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithDuplicates.Zero), "0" },
|
||||
{ nameof(EnumWithDuplicates.None), "0" },
|
||||
{ nameof(EnumWithDuplicates.One), "1" },
|
||||
{ nameof(EnumWithDuplicates.Two), "2" },
|
||||
{ nameof(EnumWithDuplicates.Duece), "2" },
|
||||
{ nameof(EnumWithDuplicates.Three), "3" },
|
||||
{ nameof(EnumWithDuplicates.MoreThanTwo), "3" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithDuplicates?),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithDuplicates.Zero), "0" },
|
||||
{ nameof(EnumWithDuplicates.None), "0" },
|
||||
{ nameof(EnumWithDuplicates.One), "1" },
|
||||
{ nameof(EnumWithDuplicates.Two), "2" },
|
||||
{ nameof(EnumWithDuplicates.Duece), "2" },
|
||||
{ nameof(EnumWithDuplicates.Three), "3" },
|
||||
{ nameof(EnumWithDuplicates.MoreThanTwo), "3" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFlags),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithFlags.All), "-1" },
|
||||
{ nameof(EnumWithFlags.Zero), "0" },
|
||||
{ nameof(EnumWithFlags.One), "1" },
|
||||
{ nameof(EnumWithFlags.Two), "2" },
|
||||
{ nameof(EnumWithFlags.Four), "4" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFlags?),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithFlags.All), "-1" },
|
||||
{ nameof(EnumWithFlags.Zero), "0" },
|
||||
{ nameof(EnumWithFlags.One), "1" },
|
||||
{ nameof(EnumWithFlags.Two), "2" },
|
||||
{ nameof(EnumWithFlags.Four), "4" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFields),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithFields.MinusTwo), "-2" },
|
||||
{ nameof(EnumWithFields.MinusOne), "-1" },
|
||||
{ nameof(EnumWithFields.Zero), "0" },
|
||||
{ nameof(EnumWithFields.One), "1" },
|
||||
{ nameof(EnumWithFields.Two), "2" },
|
||||
{ nameof(EnumWithFields.Three), "3" },
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFields?),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ nameof(EnumWithFields.MinusTwo), "-2" },
|
||||
{ nameof(EnumWithFields.MinusOne), "-1" },
|
||||
{ nameof(EnumWithFields.Zero), "0" },
|
||||
{ nameof(EnumWithFields.One), "1" },
|
||||
{ nameof(EnumWithFields.Two), "2" },
|
||||
{ nameof(EnumWithFields.Three), "3" },
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumNamesData))]
|
||||
public void GetDisplayDetails_EnumNamesAndValues_ReflectsModelType(
|
||||
Type type,
|
||||
IReadOnlyDictionary<string, string> expectedDictionary)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
// This assertion does *not* require entry orders to match.
|
||||
Assert.Equal(expectedDictionary, context.DisplayMetadata.EnumNamesAndValues);
|
||||
}
|
||||
|
||||
// Type -> expected EnumDisplayNamesAndValues
|
||||
public static TheoryData<Type, IEnumerable<KeyValuePair<string, string>>> EnumDisplayNamesData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<Type, IEnumerable<KeyValuePair<string, string>>>
|
||||
{
|
||||
{ typeof(ClassWithFields), null },
|
||||
{ typeof(StructWithFields), null },
|
||||
{ typeof(EmptyEnum), new List<KeyValuePair<string, string>>() },
|
||||
{ typeof(EmptyEnum?), new List<KeyValuePair<string, string>>() },
|
||||
{
|
||||
typeof(EnumWithDisplayNames),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("cero", "0"),
|
||||
new KeyValuePair<string, string>("uno", "1"),
|
||||
new KeyValuePair<string, string>("dos", "2"),
|
||||
new KeyValuePair<string, string>("tres", "3"),
|
||||
new KeyValuePair<string, string>("name from resources", "-2"),
|
||||
new KeyValuePair<string, string>("menos uno", "-1"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithDisplayNames?),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("cero", "0"),
|
||||
new KeyValuePair<string, string>("uno", "1"),
|
||||
new KeyValuePair<string, string>("dos", "2"),
|
||||
new KeyValuePair<string, string>("tres", "3"),
|
||||
new KeyValuePair<string, string>("name from resources", "-2"),
|
||||
new KeyValuePair<string, string>("menos uno", "-1"),
|
||||
}
|
||||
},
|
||||
{
|
||||
// Note order duplicates appear cannot be inferred easily e.g. does not match the source.
|
||||
// Zero is before None but Two is before Duece in the class below.
|
||||
typeof(EnumWithDuplicates),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.None), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Duece), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.MoreThanTwo), "3"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Three), "3"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithDuplicates?),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.None), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Duece), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.MoreThanTwo), "3"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithDuplicates.Three), "3"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFlags),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Four), "4"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.All), "-1"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFlags?),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.Four), "4"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFlags.All), "-1"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFields),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Three), "3"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.MinusTwo), "-2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.MinusOne), "-1"),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(EnumWithFields?),
|
||||
new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Zero), "0"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.One), "1"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Two), "2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.Three), "3"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.MinusTwo), "-2"),
|
||||
new KeyValuePair<string, string>(nameof(EnumWithFields.MinusOne), "-1"),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumDisplayNamesData))]
|
||||
public void GetDisplayDetails_EnumDisplayNamesAndValues_ReflectsModelType(
|
||||
Type type,
|
||||
IEnumerable<KeyValuePair<string, string>> expectedKeyValuePairs)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsMetadataProvider();
|
||||
|
||||
var key = ModelMetadataIdentity.ForType(type);
|
||||
var attributes = new object[0];
|
||||
var context = new DisplayMetadataProviderContext(key, attributes);
|
||||
|
||||
// Act
|
||||
provider.GetDisplayMetadata(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedKeyValuePairs, context.DisplayMetadata.EnumDisplayNamesAndValues);
|
||||
}
|
||||
|
||||
private class EmptyClass
|
||||
{
|
||||
}
|
||||
|
||||
private class ClassWithFields
|
||||
{
|
||||
public const int Zero = 0;
|
||||
|
||||
public const int One = 1;
|
||||
}
|
||||
|
||||
private class ClassWithProperties
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
private enum EmptyEnum
|
||||
{
|
||||
}
|
||||
|
||||
private enum EnumWithDisplayNames
|
||||
{
|
||||
[Display(Name = "tres")]
|
||||
Three = 3,
|
||||
|
||||
[Display(Name = "dos")]
|
||||
Two = 2,
|
||||
|
||||
[Display(Name = "uno")]
|
||||
One = 1,
|
||||
|
||||
[Display(Name = "cero")]
|
||||
Zero = 0,
|
||||
|
||||
[Display(Name = "menos uno")]
|
||||
MinusOne = -1,
|
||||
|
||||
[Display(Name = nameof(Test.TestResources.DisplayAttribute_Name), ResourceType = typeof(Test.TestResources))]
|
||||
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 EmptyStruct
|
||||
{
|
||||
}
|
||||
|
||||
private struct StructWithFields
|
||||
{
|
||||
public const int Zero = 0;
|
||||
|
||||
public const int One = 1;
|
||||
}
|
||||
|
||||
private struct StructWithProperties
|
||||
{
|
||||
public StructWithProperties(int id, string name)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
Assert.True(metadata.HtmlEncode);
|
||||
Assert.False(metadata.IsComplexType);
|
||||
Assert.False(metadata.IsCollectionType);
|
||||
Assert.False(metadata.IsEnum);
|
||||
Assert.False(metadata.IsFlagsEnum);
|
||||
Assert.False(metadata.IsNullableValueType);
|
||||
Assert.False(metadata.IsReadOnly);
|
||||
Assert.False(metadata.IsRequired);
|
||||
|
|
@ -50,6 +52,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
|
|||
Assert.Null(metadata.DisplayFormatString);
|
||||
Assert.Null(metadata.DisplayName);
|
||||
Assert.Null(metadata.EditFormatString);
|
||||
Assert.Null(metadata.EnumDisplayNamesAndValues);
|
||||
Assert.Null(metadata.EnumNamesAndValues);
|
||||
Assert.Null(metadata.NullDisplayText);
|
||||
Assert.Null(metadata.TemplateHint);
|
||||
Assert.Null(metadata.SimpleDisplayProperty);
|
||||
|
|
|
|||
|
|
@ -269,6 +269,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyValuePair<string, string>> EnumDisplayNamesAndValues
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyDictionary<string, string> EnumNamesAndValues
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasNonDefaultEditFormat
|
||||
{
|
||||
get
|
||||
|
|
@ -293,6 +309,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
public override bool IsEnum
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsFlagsEnum
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsReadOnly
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": "true"
|
||||
"warningsAsErrors": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.FileProviders": "1.0.0-*",
|
||||
|
|
|
|||
Loading…
Reference in New Issue