为什么我们使用ViewModels?

我最近开始从事网页开发工作。 我使用ASP .NET MVC 4和NHibernate。

在我的工作场所,我们严格使用视图模型来在控制器和视图之间来回传输数据。 视图模型不应该包含模型的任何对象。 我知道这是控制器和视图之间的一种层次。

但是,即使我们可以直接将模型的对象发送到视图(大多数情况下),我也发现编写视图模型类是重复的和冗余的。

例如,如果我想显示一个命令,我可以在控制器的操作中做到这一点 –

return View(Repository.Get<Order>(id)); 

但是相反,我必须写一个视图模型,用获取的顺序填充它,然后将其传递给视图。

所以,我的问题是,当我们可以使用模型的对象时,写视图模型的目的是什么?

对于较小的项目,你是对的。 我听到你的观点和同情 – 但是这样做有很好的理由,特别是在更大更复杂的应用中,

  • 在控制器的操作中执行所有处理是非常重要的。 然而,在你给出的例子中, Repository.Get方法可能会返回一个懒惰评估的IQueryable对象,这意味着在评估View之前,数据库不会被击中。 由于各种原因,这是不好的。 (解决方法是在仍处于控制器中时调用.ToList )。
  • “一个视图不应该包含任何非表示逻辑”和“你不应该相信视图”(因为一个视图可以由用户提供)。 通过提供一个Model对象(可能仍然连接到一个活动的DatabaseContext),一个视图可能会恶意更改您的数据库。
  • 视图的数据显示并不总是将1:1与其模型的数据映射,例如考虑用户详细信息页面:

    用户的EF模型对象表示它在数据库中的实体,所以它可能看起来像这样: User { UserId, UserName, PasswordHash, PasswordSalt, EmailAddress, CreatedDate } ,而“User details”页面上的字段将是User { UserId, UserName, Password, ConfirmYourPassword, EmailAddress } ,您是否看到区别? Ergo,你不能使用EF用户模型作为视图模型,你必须使用一个单独的类。

  • 模型操作的危险:如果让ASP.NET MVC(或任何其他框架)将模型绑定到传入的HTTP POST请求,则(以上面的用户详细信息为例),用户可以通过伪装UserId属性来重置任何人的密码值。 ASP.NET将在绑定过程中重写这个值,除非你特别的去清理它(这将会和制作单个的ViewModel一样令人厌烦),那么这个漏洞将会保留下来。

  • 在多个开发团队工作的项目中, 重要的是一切都是一致的 。 有些页面使用定制的ViewModels,而其他页面使用EF模型是不一致的,因为团队没有共同的意识,事情必须logging下来,通常是合理的。 出于同样的原因,单个开发人员可以不必将过多的XML文档放在他的源代码中,但是在团队情况下,如果不这样做,你将会崩溃。

您的情况与我们分享的方式有一些小小的解决方法,但请注意以下前提条件:

  • 您的意见可以完全信任
  • 你的观点只包含表象逻辑
  • 你的申请很大程度上是CRUD
  • 您的视图与每个EF实体模型1:1对应(即没有JOIN)
  • 您的视图只处理POST表单的简单模型,而不处理复杂模型(即对象图)

…那么你可以这样做:

  • 把所有的单向,非forms相关的数据放到你的ViewData集合中,或者在MVC 4中ViewBag (如果你是硬核的话,甚至是一个通用的ViewData<T> )。 这对于存储HTML页面标题和与主页面共享数据非常有用。
  • 使用完全评估和加载的 EF模型作为View<TModel>模型。

但谨慎使用这种方法,因为它可能会引起不一致。

那么,我开始认为每个问题的实用方法是必要的,而不是只订阅那里的纯粹的build筑标准。 您的应用程序可能需要运行在野外,并由许多开发人员为大量客户端提供服务等,这可能会引导或驱动您的架构。

  • 当你想在你的DomainModel(DataModel)和你的其他代码之间分离关注时,ViewModel是非常重要的。

模型,视图和控制器之间的依赖关系越less,越容易更改DomainModel,而不会破坏视图和控制器等中的接口契约等等。但是,再一次,它是务实的。 我喜欢这种方法,因为代码重构是系统维护的一个重要组成部分 – 重构可能包括对模型属性的简单拼写错误 – 如果依赖关系未分离,则更改可能会通过代码到达合同级别; 例如。

  • ViewModel用于翻译你的DomainModel和你Views之间的数据

存储在Informix中的date时间的简单示例必须转换为.Net DateTime。 ViewModel是做这个翻译的理想场所,不会强迫你把翻译代码放在各种不需要的地方。

一个好的devise的任何一个属性都是能够replace或修改部分实现,而对系统的其余部分影响很小或没有影响。 但是,这需要付出努力和时间来实现 – 这完全取决于您在完美的devise足够devise之间find实际的平衡

但是,有很多其他很好的理由来使用某些模式 – 但底线是这样的:

什么都不会强制你使用ViewModels … ASP.NET MVC不会强制你。 听从你内心的实用主义者的build议。

如果使用与您的ViewModel相同的模型,您的应用程序应该非常小巧,应该只包含CRUD操作。 但是,如果您正在构build大型团队(包含两个或更多的开发人员)的大型或企业应用程序,则应该具有dependency injection,服务,存储库,外观,工作单元,数据访问对象等概念。

为了简化Model和ViewModels之间的映射需求,可以使用AutoMapper https://github.com/AutoMapper/AutoMapper

或者使用nuget Install-Package AutoMapper进行安装

根据我的观点,对于执行大多数CRUD操作的复杂应用程序,在模型层之上有一层(ViewModel)是非常重要的,因为它具有以下优点:

  1. build立模型和控制器之间的松散耦合。 这样任何DataModel相关的修改都不会受到Controller的影响。
  2. 如果通过DI(使用Unity /其他框架的dependency injection)提供最高级别的IOC(控制反转)等,你已经正确地实现了你的应用程序的ViewModel图层,它也将帮助你MOQ你的ViewModels(依赖)testing只有控制器的逻辑。