为什么我们使用ViewModels?

时间:2013-01-20 09:02:40

标签: asp.net-mvc

我最近开始以网络开发人员的身份工作。我使用ASP .NET MVC 4和NHibernate。

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

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

例如,如果我想显示一个订单,我可以在控制器的操作中执行此操作 -

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

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

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

4 个答案:

答案 0 :(得分:15)

对于较小的项目,你是对的。我听到你的论点并表示同情 - 但是有充分的理由这样做,不堪重复和重复性的工作,特别是在更大更复杂的应用中:

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

    用户的EF Model对象表示其在数据库中的实体,因此它可能如下所示:User { UserId, UserName, PasswordHash, PasswordSalt, EmailAddress, CreatedDate },而“用户详细信息”页面上的字段将为User { UserId, UserName, Password, ConfirmYourPassword, EmailAddress },是吗?看到不同?因此,您无法使用EF用户模型作为视图模型,您必须使用单独的类。

  • 模型操作的危险:如果你让ASP.NET MVC(或任何其他框架)将模型绑定到传入的HTTP POST请求(使用上面的用户详细信息示例),用户可以重置任何人的伪造UserId属性值的密码。 ASP.NET将在绑定期间重写该值,除非您专门对其进行消毒(这与制作单个ViewModel一样麻烦),否则此漏洞将保留。

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

我会与您分享一些轻微的解决方法,但请注意前提条件:

  • 您的观点可以完全信任
  • 您的观点仅包含表现逻辑
  • 您的申请主要是CRUD
  • 您的观点与每个EF实体模型(即没有JOIN)1:1对应
  • 您的视图仅处理POST表单的单个简单模型,而不是复杂模型(即对象图)

...然后你可以这样做:

  • 将所有单向,非形式相关的数据放入您的ViewData集合或MVC 4中的ViewBag(如果您是硬核,则放入通用ViewData<T>) 。这对于存储HTML页面标题和与母版页共享数据非常有用。
  • 经过充分评估和加载的 EF模型用作View<TModel>型号。

但请谨慎使用此方法,因为它可能会引入不一致。

答案 1 :(得分:4)

好吧,我开始认为需要务实的方法解决每个问题,而不仅仅是订阅纯粹的架构标准。您的应用程序可能需要在野外运行,并由许多为大量客户端服务的开发人员维护,这可能会导致或推动您的架构。

  • 当您希望在DomainModel(DataModel)和其他代码之间分离关注点时,ViewModel是必不可少的。

模型,视图和控制器之间的依赖关系越少,就越容易对DomainModel进行更改,而不会破坏视图和控制器等中的接口契约等。但是再一次,这是一件好事。务实。我喜欢这种方法,因为代码重新分解是系统维护的一个重要部分 - 重构可能包括模型属性上的简单拼写错误 - 如果依赖关系没有分离,那么更改可能会通过代码波及合同级别;例如。

  • ViewModel用于在您的DomainModel和您的Views
  • 之间转换数据

存储在Informix中的日期时间的简单示例必须转换为.Net DateTime。 ViewModel是进行此翻译的理想场所,不会强迫您将翻译代码放在各种不需要的地方。

良好设计[任何事物]的一个属性是能够替换或修改实现的一部分,而对系统的其余部分影响很小或没有影响。但这需要花费精力和时间来实现 - 你可以在完美设计设计之间找到实际的平衡

但是,是的,使用某些模式还有很多其他充分的理由 - 但最重要的是:

没有什么可以强迫你使用ViewModels ...... ASP.NET MVC不会强迫你。接受你内部实用主义者的建议。

答案 2 :(得分:2)

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

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

或使用nuget安装 安装包AutoMapper

答案 3 :(得分:1)

据我所知,对于执行大多数CRUD操作的复杂应用程序,必须在Model层之上再增加一个层(ViewModel),因为它具有以下优点:

  1. 在模型和控制器之间建立松耦合。这样任何与DataModel相关的修改都不会对Controller产生影响。
  2. 如果您已正确实施了应用程序的ViewModel图层 通过提供最高水平的IOC(控制反转) DI(依赖注入使用Unity /其他框架)等,它会 还可以帮助您MOQ您的ViewModel(依赖项)仅用于测试 控制器的逻辑。