在 ASP.NET MVC 中将 Enum 绑定到 DropdownList






4.75/5 (12投票s)
将下拉列表与枚举绑定的更好方法。
引言
有时,我们在开发过程中会遇到需要从 Enum
填充 dropdownlist
的情况。 有几种方法可以做到这一点。 但是每种方法都有其优点和缺点。 在编写代码时,应该始终牢记 SOLID 和 DRY 原则。 我将展示两种方法,您将能够了解哪种方法更好,以及为什么更好。
方法 1 (典型方法)
在我看来,第一种方法不是很优雅,但它肯定会实现我们的目标。
考虑以下我们要在下拉列表中填充的 Enum
public enum eUserRole : int
{
SuperAdmin = 0,
PhoenixAdmin = 1,
OfficeAdmin = 2,
ReportUser = 3,
BillingUser = 4
}
因此,通常,我们通过在 Action 中以以下方式添加每个 Enum
值来创建 SelectList
var enumData = from eUserRole e in Enum.GetValues(typeof(eUserRole))
select new
{
ID = (int)e,
Name = e.ToString()
};
现在,将其设置为 ViewBag
,以便我们可以在 View
中使用它
ViewBag.EnumList = new SelectList(enumData,"ID","Name");
现在在 View
中
@Html.DropDownList("EnumDropDown",ViewBag.EnumList as SelectList)
方法 1 中的问题
上述方法的问题是,每当我们有 Enum
要与某些 Html Helper 绑定时,我们都必须编写上述 Linq 查询代码,以在 Action 中获取 enum
的所有值,这有点麻烦,需要一遍又一遍地重写。 接下来,我们将看到如何使它更好、更可重用。
方法 2
现在,这里有一种使用扩展方法和泛型来实现它的优雅方法,它将返回 Enum
值作为任何类型的 Enum
的 SelectList
public static class ExtensionMethods
{
public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
.Select(x =>
new SelectListItem
{
Text = Enum.GetName(typeof(TEnum), x),
Value = (Convert.ToInt32(x)).ToString()
}), "Value", "Text");
}
}
现在,我们只需要以这种方式在 Action 中对任何 Enum
调用它
ViewBag.EnumList = eUserRole.SuperAdmin.ToSelectList();
我们也可以直接在 View
中使用它,我们只需要包含命名空间,以防它在单独的命名空间中
@Html.DropDownList("EnumDropDown",eUserRole.SuperAdmin.ToSelectList())
当用户编辑记录时,您可能需要在 dropdownlist
中设置选定的值。
我们可以根据我们的需求扩展扩展方法。
使用选定值参数重载
以下是扩展方法重载,用于传递选定的值,以防我们想要设置选定的值,您也可以根据需要编写其他重载
public static class ExtensionMethods
{
public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj,object selectedValue)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
.Select(x =>
new SelectListItem
{
Text = Enum.GetName(typeof(TEnum), x),
Value = (Convert.ToInt32(x)).ToString()
}), "Value", "Text",selectedValue);
}
}
并以这种方式在 View
中使用
@Html.DropDownList("EnumDropDownWithSelected",
eUserRole.SuperAdmin.ToSelectList((int)eUserRole.OfficeAdmin))
现在,下拉列表将默认选择 OfficeAdmin
。
在大多数情况下,我们不希望在下拉列表中显示 Enum
值,而是希望显示用户友好的术语作为下拉文本。 为此,我们可以按照以下方式为 Enum
编写 Attribute
创建一个从 Attribute
类型继承的自定义类
public class EnumDisplayNameAttribute : Attribute
{
private string _displayName;
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
}
现在,在 Enum
上使用 attribute
public enum eUserRole : int
{
[EnumDisplayName(DisplayName="Super Admin")]
SuperAdmin = 0,
[EnumDisplayName(DisplayName = "Phoenix Admin")]
PhoenixAdmin = 1,
[EnumDisplayName(DisplayName = "Office Admin")]
OfficeAdmin = 2,
[EnumDisplayName(DisplayName = "Report User")]
ReportUser = 3,
[EnumDisplayName(DisplayName = "Billing User")]
BillingUser = 4
}
现在,我们需要修改或编写另一个扩展方法,因为现在我们需要选择 DisplayName
属性的值。
我们现在有两个扩展方法,一个返回特定的 Enum
值 DisplayName Attribute
值,第二个返回针对 Enum
的 SelectList
public static class ExtensionMethods
{
public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj)
where TEnum : struct, IComparable, IFormattable, IConvertible // correct one
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
.Select(x =>
new SelectListItem
{
Text = x.DisplayName(),
Value = (Convert.ToInt32(x)).ToString()
}), "Value", "Text");
}
public static string DisplayName(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
EnumDisplayNameAttribute attribute
= Attribute.GetCustomAttribute(field, typeof(EnumDisplayNameAttribute))
as EnumDisplayNameAttribute;
return attribute == null ? value.ToString() : attribute.DisplayName;
}
}
方法 2 的问题
第二种方法比第一种方法好得多,但是方法 2 仍然存在一个问题,那就是我们在扩展中硬编码了 Attribute
类型,我们很可能拥有多个 Enum
属性,并且可以使用不同的 Enum
装饰它们,并且在此类 Enum
上调用此扩展方法将无法正常工作。
方法 3
因此,我提出了一个更好的实现,以便消费者可以传递属性类型和需要使用的属性的属性。
我们将添加另一个扩展方法,该方法返回属性值,并且它是通用的,因此用户可以指定属性类型本身
public static string AttributeValue<TEnum,TAttribute>(this TEnum value,Func<TAttribute,string> func)
where T : Attribute
{
FieldInfo field = value.GetType().GetField(value.ToString());
T attribute = Attribute.GetCustomAttribute(field, typeof(T)) as T;
return attribute == null ? value.ToString() : func(attribute);
}
我们将在返回 Enum
作为 SelectList
的扩展方法中使用此扩展
public static System.Web.Mvc.SelectList ToSelectList<TEnum,TAttribute>
(this TEnum obj,Func<TAttribute,string> func,object selectedValue=null)
where TEnum : struct, IComparable, IFormattable, IConvertible
where TAttribute : Attribute
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
.Select(x =>
new SelectListItem
{
Text = x.AttributeValue<TEnum,TAttribute>(func),
Value = (Convert.ToInt32(x)).ToString()
}),
"Value",
"Text",
selectedValue);
}
现在,消费者可以通过传递属性及其要用于显示名称的属性来使用它,我们的 View 代码现在看起来像
@Html.DropDownList("EnumDropDownWithSelected", eUserRole.SuperAdmin.ToSelectList<eUserRole,
EnumDisplayNameAttribute>(attr=>attr.DisplayName,(int)eUserRole.OfficeAdmin))