【问题标题】:ASP.NET MVC ViewModel with SelectList(s) best practice具有 SelectList(s) 最佳实践的 ASP.NET MVC ViewModel
【发布时间】:2011-12-15 03:49:53
【问题描述】:

我注意到在 NerdDinner 应用程序中,如果 ModelState 对于晚餐无效,它只会返回模型的视图:

        if (ModelState.IsValid) {
            ...
            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }

        return View(dinner);

但是,在我的应用程序中,模型(在这种情况下为视图模型)包含多个 SelectList。这些列表此时没有实例化,因为这个视图模型只是从表单提交中填充的。在将 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 填充代码放在我的控制器中似乎没有意义。我是否应该在我的业务逻辑中创建一个公共方法,仅用于在我的视图模型上执行此类操作?

【问题讨论】:

    标签: asp.net-mvc selectlist


    【解决方案1】:

    如果 ModelState 无效,我的控制器会填充模型上的 SelectList。

    在关注点分离之后,您的业务类不应该对视图模型有任何了解。如果您的视图需要员工列表,您的控制器会从业务层获取员工列表并创建您的视图所需的 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); 
    } 
    

    更新

    如果您的选择列表人口代码正在确定要呈现哪些选项,我认为您可能应该将其移至业务层中的服务。如果重用性是大问题,鲁昂的回答看起来最有可能重用。

    【讨论】:

      【解决方案2】:

      我个人喜欢保持简单:-

      [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.
      }
      

      如果填充选择列表的逻辑很疯狂,那么可能值得转移到一个单独的类或其他任何类,但作为第一步,这是最好的起点。

      【讨论】:

        【解决方案3】:

        即使模型无效,我也会用来填写列表。另一种可能的解决方案是让操作返回 json 信息并通过 ajax 构建选择。有时我也求助于静态属性/缓存集合。我想这总是取决于具体情况。

        PS:您可以在每个操作中使用本地模型,因此我可以将初始化留在模型构造函数中。 (我经常用[NonAction] 实用程序覆盖基本模型)。

        例如,我有一个在您的应用程序中广泛使用的员工列表。

        我在基本控制器中添加了一些实用方法来构建 SelectListItems 等。由于每个模型都继承自基础模型,因此我在应用程序中几乎无处不在。当然,集合是通过专门的业务对象填充的。

        【讨论】:

          【解决方案4】:

          你没有说你想要多少可重用性。但就个人而言,我喜欢“清晰”(不要入侵控制器)和尽可能可重用的东西,这在 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);
              }
          

          【讨论】:

          • 这看起来很有趣。我将不得不玩弄它。 MVC 3 中对过滤器的 DI 支持应该使这更容易。
          • 我也喜欢这种方法,但是因为我有可以采用多种模型的动作方法,所以这行不通。我正在使用自定义模型绑定器,所以我没有以典型的方式使用 MVC。不过,这似乎在大多数情况下都可以使用。我只是不确定在我的控制器中放置一堆属性。
          • IMO - 必须创建自己的 html 助手来填充选择列表既不是“清晰”,也不是“可重用”。没有比@John Foster 的回答更清楚了(使用返回所需选择列表的方法。)
          【解决方案5】:

          我所做的是我在一个返回 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 以发送到视图。它使重用已配置的 SelectList 变得非常容易。此外,如果视图需要遍历对象列表,则可以使用相同的函数来获取列表。这是我发现的最简单、最方便的方法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2023-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-21
            • 2010-10-14
            相关资源
            最近更新 更多