我注意到在NerdDinner应用程序中,如果ModelState对于晚餐无效,它只会返回模型的视图:
if (ModelState.IsValid) {
...
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
return View(dinner);
但是,在我的应用程序中,模型(在这种情况下的视图模型)包含多个SelectLists。此时未实例化这些列表,因为此视图模型刚刚从表单提交中填充。在将这些SelectLists发送给用户之前,重新填充此SelectLists的建议方法是什么?
这就是我希望控制器做的事情:
public ActionResult Save(MyModel model)
{
if (ModelState.IsValid)
{
businessClass.Save(model);
return RedirectToAction("Index", "Home");
}
// This won't work because model has uninstantiated SelectLists
return View("MyView", model);
}
如果ModelState无效,我不想将模型发送到我的业务逻辑,但将SelectList填充代码放在我的控制器中似乎没有意义。我应该在业务逻辑中创建一个公共方法,仅仅是为了在我的视图模型上做这种事情吗?
答案 0 :(得分:14)
就个人而言,我喜欢保持简单: -
[HttpGet]
public Edit(int id) {
EditForm form = new EditForm();
// Populate from the db or whatever...
PopulateEditPageSelectLists(form);
return View(form);
}
[HttpPost]
public Edit(EditForm form) {
if (ModelState.IsValid) {
// Do stuff and redirect...
}
PopulateEditPageSelectLists(form);
return View(form);
}
public void PopulateEditPageSelectLists(form) {
// Get lookup data from the db or whatever.
}
如果填充选择列表的逻辑是疯狂的,那么移动到一个单独的类或任何它可能是值得的,但作为第一步,这是最好的起点。
答案 1 :(得分:5)
你不想说你想要多少可重用性。但就个人而言,我喜欢“清晰”(不要侵入控制器)和尽可能重用的东西,而在MVC意味着 - 过滤器。
看看这个:
public class SupplyLanguagesAttribute : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["languagesList"] =
someService.LoadLanguagesAsDictionary();
base.OnActionExecuting(filterContext);
}
}
然后您只需将它用于您可能需要语言的每种操作方法:
[SupplyLanguages]
public ActionResult DoSomething()
{
...
}
然后在视图中,您可以直接从ViewData使用DropDownList的数据,或者您甚至可以“包装”它(并避免在视图中使用“魔术字符串”),使用自定义可重用的DropDown:
public static MvcHtmlString LanguageDropDown(this HtmlHelper html, string name, object selectValue, bool defaultOption = false)
{
var languages = html.ViewData["languagesList"] as IDictionary<string,string>;
if (languages == null || languages.Count() == 0)
throw new ArgumentNullException("LanguageDropDown cannot operate without list of languages loaded in ViewData. Use SupplyLanguages filter.");
var list = new SelectList(languages, "Key", "Value", selectValue);
return SelectExtensions.DropDownList(html, name, list);
}
答案 2 :(得分:1)
如果ModelState无效,我的控制器会填充模型上的SelectLists。
关注分离后,您的业务类根本不了解视图模型。如果您的视图需要员工列表,您的控制器将从您的业务层获取员工列表,并创建您的视图所需的SelectList。
public ActionResult Save(MyModel model)
{
if (ModelState.IsValid)
{
businessClass.Save(model);
return RedirectToAction("Index", "Home");
}
model.PossibleEmployees
= _employeeRepository.All().Select(e =>
new SelectListItem{Text=e.Name,
Value=e.Id});
return View("MyView", model);
}
如果您的选择列表填充代码确定要显示的WHICH选项,我认为您可能应将其移至业务层中的服务。如果可重用性是一个大问题,那么鲁肯的答案似乎最有可能被重用。
答案 3 :(得分:0)
即使模型无效,我也会用它来填充列表。另一个可能的解决方案是让一个动作返回json信息并通过ajax构建select。因此,我也使用了静态属性/缓存集合。我想这总是取决于具体情况。
PS:您可以在每个操作中使用本地模型,因此我可以在模型构造函数中保留初始化。 (通常我也会使用[NonAction]
实用程序覆盖基础模型。
例如,我在您的应用程序中广泛使用了一个Employee列表。
我在基本控制器中添加了一些实用工具方法来构建SelectListItems等。由于每个模型都继承自基础,我在应用程序中几乎无处不在。当然,收藏品是通过专门的商业目标填写的。
答案 4 :(得分:0)
我所做的是在类中返回一个SelectList的静态函数。该方法接受Enum值,该值定义要返回的SelectList。在View中,DropDownList或DropDownListFor函数调用此函数来获取SelectList。
静态函数如下所示:
class HelperMethods
{
enum LookupType {Users, Companies, States};
public static SelectList CommonSelectList(LookupType type, int? filterValue = null)
//filterValue can be used if the results need to be filtered in some way
var db = new WhateverEntities();
switch (type)
{
case LookupType.Users:
var list = db.Users.OrderBy(u => u.LastName).ToList()
return new SelectList(list, "ID", "FullName")
break;
case LookupType.Companies
var list = db.Companies.OrderBy(u => u.Name).ToList()
return new SelectList(list, "ID", "Name")
break;
//and so on...
}
}
}
该视图包含:
@Html.DropDownListFor(m => m.UserID, HelperMethods.CommonSelectList(LookupType.Users))
这样,模型和控制器不需要代码来配置SelectList以发送到View。它使重用已经配置的SelectList变得非常容易。此外,如果View需要循环遍历对象列表,则可以使用此相同的函数来获取该列表。这是我发现这种方式最简单,最方便的方式。