在ASP.NET MVC应用程序开发中,经常需要将枚举类型转换为下拉列表(DropDownList)的选项。这个过程可以通过多种方式实现,每种方式都有其优缺点。在编写代码时,应始终牢记SOLID和DRY原则。本文将展示两种实现方式,并分析哪一种更好以及为什么更好。
在看来,第一种方法虽然不够优雅,但它确实能够实现想要的功能。考虑以下枚举,希望将其填充到下拉列表中:
public enum eUserRole : int
{
SuperAdmin = 0,
PhoenixAdmin = 1,
OfficeAdmin = 2,
ReportUser = 3,
BillingUser = 4
}
通常,会在动作中通过添加每个枚举值的方式来创建SelectList:
var enumData = from eUserRole e in Enum.GetValues(typeof(eUserRole))
select new
{
ID = (int)e,
Name = e.ToString()
};
ViewBag.EnumList = new SelectList(enumData, "ID", "Name");
然后在视图中使用:
@Html.DropDownList("EnumDropDown", ViewBag.EnumList as SelectList)
方法1的问题在于,每当需要将枚举绑定到某个Html Helper时,都需要编写上述Linq查询代码来获取枚举的所有值,这有点繁琐,需要一次又一次地重写相同的东西。接下来将看到如何使其更好且可重用。
现在,这里有一种更优雅的方式来实现它,使用扩展方法和泛型,它将返回任何类型的枚举值作为SelectList:
public static class ExtensionMethods
{
public static System.Web.Mvc.SelectList ToSelectList(this TEnum obj)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType()
.Select(x =>
new SelectListItem
{
Text = Enum.GetName(typeof(TEnum), x),
Value = (Convert.ToInt32(x)).ToString()
}),
"Value",
"Text");
}
}
现在,只需要在动作中调用它:
ViewBag.EnumList = eUserRole.SuperAdmin.ToSelectList();
也可以直接在视图中使用它,只需要包含命名空间,以防它在单独的命名空间中:
@Html.DropDownList("EnumDropDown", eUserRole.SuperAdmin.ToSelectList())
第二种方法比第一种方法要好得多,但方法2还有一个问题,那就是在扩展中硬编码了属性类型,这是相当可能的,有多个枚举属性,并且它们可以用不同的枚举装饰,调用这个扩展方法在那些上面可能不会很好地工作。
因此,想出了一个更好的实现,以便消费者可以传递属性类型和需要使用的属性属性。将添加另一个扩展方法,它返回属性值,并且它是泛型的,用户可以指定属性类型本身:
public static string AttributeValue(this TEnum value, Func 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);
}
这个扩展将在返回枚举为SelectList的扩展方法中使用:
public static SelectList ToSelectList(this TEnum obj, Func func, object selectedValue = null)
where TEnum : struct, IComparable, IFormattable, IConvertible
where TAttribute : Attribute
{
return new SelectList(Enum.GetValues(typeof(TEnum)).OfType()
.Select(x =>
new SelectListItem
{
Text = x.AttributeValue(func),
Value = (Convert.ToInt32(x)).ToString()
}),
"Value",
"Text",
selectedValue);
}
现在消费者可以通过传递属性及其要用于显示名称的属性来使用它,视图代码现在看起来像这样:
@Html.DropDownList("EnumDropDownWithSelected", eUserRole.SuperAdmin.ToSelectList(attr => attr.DisplayName, (int)eUserRole.OfficeAdmin))