【问题标题】:ASP.NET MVC - How exactly to use View ModelsASP.NET MVC - 如何准确地使用视图模型
【发布时间】:2013-05-09 00:58:45
【问题描述】:

假设我有一个允许编辑用户详细信息的页面,所以我有一个这样的 ViewModel:

public class UserViewModel {
    public string Username { get; set; }
    public string Password { get; set; }
    public int ManagerId { get; set; }
    public string Category { get; set; }
}

因此,在我的 EditUser 操作中,我可以让模型绑定器将其传回,然后我可以将其映射到域模型:

public ActionResult EditUser(UserViewModel user) {
    ...

但是,显示表单的页面还需要详细信息,例如经理和类别列表,以便为这些字段提供下拉菜单。它还可能在边栏中显示其他用户的列表,以便您可以在正在编辑的不同用户之间切换。

那么我有另一个视图模型:

public class ViewUserViewModel {
    public UserViewModel EditingUser { get; set; }
    public IEnumerable<SelectListItem> Managers { get; set; }
    public IEnumerable<SelectListItem> Categories { get; set; }
    public IEnumerable<SelectListItem> AllUsers { get; set; }
}

这是正确的方法吗?他们都是视图模型吗?如果是这样,是否有我应该使用的命名约定,以便区分类似于模型的 VM 和仅包含页面数据的 VM?

我是不是都搞错了?

【问题讨论】:

    标签: asp.net-mvc viewmodel


    【解决方案1】:

    我个人更喜欢将页面呈现所需的所有信息放在 ViewModel 中,因为这就是 ViewModel 的目的 - 为 View 提供所有数据。所以我的UserViewModel 将包含ManagersCategoriesAllUsers 的属性,并且控制器会在将 ViewModel 传递给视图之前填充这些集合。

    这基本上就是您所做的 - 它只是从等式中删除了额外的 ViewModel。

    我还看到其他程序员使用 ViewData 将下拉列表发送到视图,但我不喜欢这样,因为 ViewData 不是强类型,而 ViewModel 是。

    【讨论】:

      【解决方案2】:

      “视图模型”只是一种模式。这个名字没有什么神奇之处,但通常任何传递给视图的类(无论是用于简单显示数据还是用于表单提交)都被称为“视图模型”,并命名为 FooViewModel 或 @987654322 @ 表示它是“视图模型”模式的一部分。

      我不想对你过于哲学化,但我认为一些关于游戏模式的参考会有所帮助。 ASP.NET MVC 显然足够鼓励 MVC(模型-视图-控制器)架构模型。在 MVC 中,模型是所有应用程序的业务逻辑 的容器。控制器负责处理请求、获取模型、使用该模型渲染视图并返回响应。这似乎是一个很大的责任,但实际上框架在幕后处理了大部分这些,所以控制器通常(并且应该)对代码非常轻。他们负责将所有东西连接起来的最低限度的功能。最后,View 负责创建 UI 层,允许用户与 Model 中的数据进行交互。它对数据本身负责,也不应该负责(ViewData/ViewBag 在这里是一个相当大的违规行为,至少与开发人员在实践中最终使用它的方式一样多)。

      因此,这意味着您的大部分应用程序逻辑都应该在您的模型中,这通常是一件好事。然而,由于模型是应用程序数据的避风港,它通常会保存在数据库或类似的数据库中。这会产生一些利益冲突,因为您现在需要在哪些数据应该被持久化和哪些数据应该仅用于显示目的之间进行平衡。

      这就是视图模型的用武之地。MVVM(模型-视图-视图模型)是一种与 MVC 有点平行的模式,它认识到一个模型来统治所有方法中的固有问题。我不会在这里详细介绍,因为 MVC 不使用这种模式。但是,大多数 ASP.NET MVC 开发人员都选择了 MVVM 的视图模型。您最终得到的是一个数据库支持的entity(传统模型),然后通常是许多不同的视图模型,它们代表处于各种状态的实体。这允许您的模型包含与持久性相关的业务逻辑,而视图模型包含与显示、创建和更新该模型相关的业务逻辑。

      我有点偏离了轨道,但总而言之,你所做的事情是完全可以接受的。事实上,这是一种很好的做法。根据应用程序的需要创建尽可能多的视图模型,并使用它们实际存储视图所需的数据和业务逻辑。 (包括SelectLists 之类的东西。您的控制器和视图都不需要知道如何为下拉菜单创建SelectList。)

      【讨论】:

      • 关于这个主题的最佳解释。你应该把它放在博客上! +1
      • 您提到模型应该负责应用程序的业务逻辑。所谓业务,您可能是指所有数据准备、查询、过滤、将一个模型投影到另一个模型或特定 ViewModel。然后将这样准备好的 ViewModel 由控制器传递给 View。你在身体上是如何做到的?您如何设计模型以开展业务?例如,您是否将所有控制器方法移至表示视图模型的类?目前,我在控制器中有很多功能和“业务”,它们可以完成所有的细节。谢谢
      • 坦率地说,您不能在 ASP.NET MVC 中真正做到这一点。要真正理解我在说什么,请在 Ruby on Rails 中构建一个示例应用程序。 RoR 非常严格地遵循 MVC 模式,您将确切地看到他们的模型中有多少内容。另一方面,ASP.NET MVC 只是松散地遵循 MVC。您的“模型”将是实体类、视图模型以及诸如存储库或服务之类的某种组合。你应该尽量保持你的控制器精简,你只是不能将所有逻辑移到一个类中。
      【解决方案3】:

      我如何在快捷方式中做到这一点:

      1. 为页面上的每个表单创建单独的 ViewModel 类,然后使用 PartialViews 将这些类渲染为@{Html.RenderPartial("PartialName", Model.PartialModel);}
      2. 如果页面包含诸如 html 元数据之类的内容,我会为元数据创建单独的类并将其放在页面的部分中。
      3. 诸如“我应该把它放在单独的班级吗?”之类的其他情况。是你的判断。

      例如,您的页面有某种登录/注册栏或弹出窗口。

      public class SomePageViewModel
      {
          public RegisterBarVM Register { get; set; }
          public LoginBarVM LoginBar { get; set; }
      
          public MetasVM Metas { get; set; }
          public string MaybePageTitle { get; set;}
          public string MaybePageContent { get; set;}
      
          [HiddenInput(DisplayValue = false)]
          public int IdIfNeeded { get; set; }
      
          public IEnumerable<SelectListItem> SomeItems {get; set;}
          public string PickedItemId { get;set; }
      }
      
      public class RegisterBarVM
      {
          public string RegisterUsername {get;set;}
          public string RegisterPassword {get;set;}
          //...
      }
      
      public class LoginBarVM
      {
          public string LoginUserame {get;set;}
          public string LoginPassword {get;set;}
          //...
      }
      
      //cshtml
      @model yourClassesNamespace.SomePageViewModel
      @{
          Html.RenderPartial("LoginBar", Model.LoginBar); //form inside
          Html.RenderPartial("RegisterBar", Model.RegisterBar); //form inside
      
          using(Html.BeginForm())
          {
              @Html.EditorFor(m => m.IdIfNeeded)
              @Hmtl.EditorFor(m => m.MaybePageTitle)
              @Hmtl.EditorFor(m => m.MaybePageContent)
      
              @Hmtl.DropDownListFor(m => m.PickedItemId, new SelectList(Model.SomeItems))
      
              <input type="submit" value="Update" />
          }
      }
      
      @section Metas {
          @{Html.RenderPartial("Meatas", Model.Metas}
      }
      

      关于编辑器模板Brad Wilsons Blog,只需 google 或查找有关显示/编辑器模板和 HtmlHelpers 的堆栈资源。它们对于构建一致的网站都非常有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-30
        • 2021-07-16
        • 1970-01-01
        • 1970-01-01
        • 2010-11-24
        • 1970-01-01
        • 2011-07-15
        • 2010-11-18
        相关资源
        最近更新 更多