假设我有一个允许编辑用户详细信息的页面,所以我有一个这样的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) {
...
但是,显示表单的页面还需要详细信息,例如manager和Categories列表,以便为这些字段提供下拉列表。它还可以在侧边栏中显示其他用户的列表,以便您可以在正在编辑的不同用户之间切换。
然后我有另一个视图模型:
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 ?
我都搞错了吗?"View Model"只是一个模式。这个名称并没有什么神奇之处,但通常任何传递给视图的类(无论是简单地显示数据还是为了表单提交的目的)都被称为"视图模型",并命名为FooViewModel
或FooVM
,以表明它是"视图模型"模式的一部分。
我不想对你讲得太哲学,但我认为关于游戏模式的一点参考会有所帮助。ASP。NET MVC显然足够鼓励MVC(模型-视图-控制器)体系结构模型。在MVC中,模型是所有应用程序业务逻辑的容器。控制器负责处理请求、获取模型、呈现带有该模型的视图并返回响应。这看起来好像有很多责任,但实际上框架在幕后处理了大部分,所以控制器通常(也应该)非常少的代码。他们负责将所有东西连接起来的最基本的功能。最后,视图负责创建允许用户与模型中的数据交互的UI层。不是负责数据本身,也不应该(ViewData/ViewBag在这里是一个相当大的违反,至少在它最终被开发人员在实践中使用的方式)。
所以,那意味着你的大部分应用程序逻辑应该在你的模型中,通常这是件好事。但是,由于模型是应用程序数据的避风港,因此通常将其持久化到数据库或类似的数据库中。这就产生了一些利益冲突,因为您现在需要开始平衡哪些数据应该持久化,哪些数据应该仅为显示而存在。
这就是视图模型的作用。MVVM(模型-视图-视图模型)是一种与MVC类似的模式,它认识到"一个模型到所有规则"方法的固有问题。我不会在这里讨论太多细节,因为MVC不使用这种模式。然而,大多数ASP。. NET MVC开发人员采用了MVVM的视图模型。你最终得到的是一个数据库支持的实体(传统模型),然后通常是许多不同的视图模型,它们以不同的状态表示该实体。这允许您的模型包含与持久性相关的业务逻辑,而视图模型包含与显示、创建和更新该模型相关的业务逻辑。
我有点跑题了,但总而言之,你所做的是完全可以接受的。事实上,这是很好的练习。根据应用程序的需要创建尽可能多的视图模型,并使用它们实际存储视图所需的数据和业务逻辑。(这包括SelectList
之类的东西。您的控制器和视图都不需要知道如何为下拉菜单创建SelectList
我如何在快捷方式中做到这一点:
- 为页面上的每个表单创建单独的ViewModel类,然后我用PartialViews渲染这些类作为
@{Html.RenderPartial("PartialName", Model.PartialModel);}
。 - 如果页面包含像html元这样的东西,我为元创建一个分离的类,并把它放在页面的部分。
- 其他情况,如"我应该把这个放在单独的类中吗?"是你的判断。
例如你有一个页面它有一些登录/注册栏或者弹出窗口等等
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的博客,只需谷歌或寻找有关显示/编辑器模板和HtmlHelpers的堆栈资源。
我个人更喜欢将页面呈现所需的所有信息放在ViewModel中,因为这是ViewModel的目的——为视图提供所有数据。因此,我的UserViewModel
将包含Managers
, Categories
和AllUsers
的属性,控制器将在将ViewModel传递给视图之前填充这些集合。
这就是你所做的——它只是从等式中删除了额外的ViewModel。
我也见过其他程序员使用ViewData发送下拉列表到视图,但我不喜欢,因为ViewData不是强类型的,而ViewModel是。