37

假设我有一个允许编辑用户详细信息的页面,所以我有一个这样的 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?

我把这一切都搞错了吗?

4

3 回答 3

95

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

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

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

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

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

于 2013-05-14T18:28:10.453 回答
20

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

  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 博客,只需 google 或查找有关显示/编辑器模板和 HtmlHelpers 的堆栈资源。它们对于构建一致的网站都非常有用。

于 2013-05-14T21:20:57.423 回答
9

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

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

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

于 2013-05-14T16:38:11.480 回答