一个 ASP.Net MVC 中适用于所有下拉列表的 EditorTemplate





5.00/5 (5投票s)
在 OnResultExecuting 中获取 DropDownList 项。
引言
每个 ASP.Net MVC 程序员都知道当将 ViewModel 发送到相应的 View 时,需要简化 DropDownList。
这就是我如何做的,并且还有许多其他例子可以自动化这个无聊的任务。
背景
几乎每个编辑 ViewModel 至少需要一个 DropDownList 来处理 Model 或数据库中的 ForeignKey。
几乎所有 ASP MVC 程序员都使用 Html.DropDownList(...) 或 Html.DropDownListFor(...) 帮助器方法。 为了填充这个 DropDownList,他们从 Controller 中的 Data Store 中提取项目,并将它们放入 ViewData 或 ViewBag,然后在帮助器方法中使用这些值。
但现在我将使用一个 EditorTemplate 来处理应用程序中的所有 DropDownList。
代码示例
这就是我试图做的
public class PersonEditor
{
public int Id { get; set; }
public string Name { get; set; }
[DropDown("GetServices", 1)]
public int? ServiceId { get; set; }
}
然后我需要我的 Controller 在 View 执行之前为我处理这个导航属性,并传递我添加的任何参数,例如本例中的数字 1。
类似这样
[FillDropDowns]
public ActionResult Edit()
{
var viewModel = new PersonEditor() {ServiceId = 3};
return View(viewModel);
}
很明显,我们需要一个 Attribute 来装饰导航属性,我们还需要一个 ActionFilter,它将在 View 呈现之前执行以填充 ViewData 字典中的项目。
假设这是我们的简单的 DropDownListServiceLayer
public class DropDownListService
{
..........
public IEnumerable<SelectListItem> GetServices (int typeId)
{
return context.Services
.Where(m => m.Type == typeId)
.Select(m => new SelectListItem {Text = m.Name, Value = m.Id.ToString()});
}
}
然后我们需要在 View 呈现之前调用 GetServices 方法,并将结果存储在某个 ViewData 键中,以便在 View 中检索它。
让我们继续吧 :).
DropDownAttribute
代码
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class DropDownAttribute : UIHintAttribute
{
private readonly Type _serviceType;
private readonly string _methodName;
private readonly object[] _arguments;
public DropDownAttribute(string methodName, params object[] arguments)
: this(methodName, "DropDown", typeof(DropDownListService), arguments)
{
}
public DropDownAttribute(string methodName, string templateName, Type serviceType, params object[] arguments)
: base(templateName)
{
_serviceType = serviceType;
_comboBoxServiceMethods = methodName;
_arguments = arguments;
}
public IEnumerable<SelectListItem> GetMethodResult()
{
if (_serviceType == null)
throw new NoNullAllowedException("Service class type is needed.");
try
{
var serviceInstance = Activator.CreateInstance(_serviceType);
var methodInfo = _serviceType.GetMethod(_methodName);
return methodInfo.Invoke(serviceInstance, _arguments) as IEnumerable<SelectListItem>;
}
catch (Exception)
{
throw;
}
}
}
很明显,这个 Attribute 继承自 "UIHintAttribute" 以获取 Editor 模板的名称。 我们需要 Service 类型,它将为我们提供 IEnumerable<SelectListItem>,并且有一个用于默认 Service 类型的重载构造函数。
然后我们需要方法名称和它将接受的参数,因此我添加了 params 关键字以将所需参数传递给此方法。
GetMethodResult 方法将从 ActionFilter 中调用以执行它并获取结果。
ActionFilter
代码
public class FillDropDowns : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var viewModel = filterContext.Controller.ViewData.Model;
if (viewModel != null)
setLists(viewModel.GetType(), filterContext.Controller.ViewData);
base.OnResultExecuting(filterContext);
}
private static void setLists(Type viewModelType, IDictionary<string, object> viewData)
{
foreach (var property in viewModelType.GetProperties())
{
if (!(property.PropertyType.IsClass && !(property.PropertyType == typeof(string))))
{
var att = (DropDownAttribute)GetCustomAttribute(property, typeof(DropDownAttribute));
if (att != null)
{
var viewDataKey = "DDKey_" + property.Name;
viewData[viewDataKey] = viewData[viewDataKey] ?? att.GetMethodResult();
}
}
else
{
setLists(property.PropertyType, viewData);
}
}
}
}
ActionFilter 将在 ViewModel 中查找任何使用 DropDownAttribute 装饰的属性,如果 ViewData 字典中尚没有这些列表,那么我们将调用 GetMethodResult 并在 ViewData 中使用相应的 Key 存储此列表。
您可能注意到,我添加了 "DDKey_" 作为此 key 的前缀,这是因为 ASP.Net MVC 3 在 ViewData 中设置与您的属性名称相同的名称时存在问题。
另一件事是,如果您有嵌套的 ViewModels,那么递归将循环遍历它们并填充其导航属性。
Editor 模板
在 EditorTemplates 文件夹中添加新视图,作为所有下拉导航属性的编辑器。 并且当我们使用 UIHint Attribute 装饰我们的属性时,EditorTemplate 文件应该命名为 "DropDown",这就是它的简单代码
@model object @Html.DropDownListFor(m => m, Html.GetAutomatedList(m => m).SetSelected(Model))
阅读下一部分,了解 "GetAutomatedList" 和 "SetSelected" 帮助器方法,
Html 扩展帮助器
最后,我们只需要两个帮助器方法来完成工作
public static class HtmlExtensions
{
public static IEnumerable<SelectListItem> SetSelected(this IEnumerable<SelectListItem> selectList, object selectedValue)
{
selectList = selectList ?? new List<SelectListItem>();
if (selectedValue == null)
return selectList;
var vlaue = selectedValue.ToString();
return selectList
.Select(m => new SelectListItem
{
Selected = string.Equals(vlaue, m.Value),
Text = m.Text,
Value = m.Value
});
}
public static IEnumerable<SelectListItem> GetAutomatedList<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return ((IEnumerable<SelectListItem>) htmlHelper.ViewData["DDKey_" + metadata.PropertyName]);
}
}
GetAutomatedList 扩展方法将通过 ViewModel 属性名称从 ViewData 获取所需的列表。而另一个方法 SetSelected 只是将 Selected 属性设置为 SelectListItem。
最后
这是我的第一篇文章,正如您刚刚发现的那样,我的语言不是很好,但我希望这个想法是清晰且有帮助的。
感谢您的时间。