26

我最近开始作为一名网络开发人员工作。我使用 ASP .NET MVC 4 和 NHibernate。

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

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

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

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

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

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

4

4 回答 4

18

对于较小的项目,您是对的。我听到你的论点并表示同情 - 然而,这是有充分理由的,繁重和重复的工作,尤其是在更大和更复杂的应用程序中:

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

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

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

  • 在多个开发人员在团队情况下工作的项目中,一切都保持一致是很重要的。某些页面使用定制的 ViewModel 而其他页面使用 EF 模型并不一致,因为团队没有共同的意识,事情必须记录在案并且通常是有意义的。出于同样的原因,单个开发人员无需在其源代码中放入过多的 XML 文档就可以侥幸逃脱,但在团队情况下,如果您不这样做,您将分崩离析。

在您的情况下,我将与您分享一个轻微的解决方法,但请注意先决条件:

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

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

  • 将所有单向、与表单无关的数据放入您的ViewData集合中,或者放入 MVC 4 中(如果您是铁杆的话ViewBag,甚至是通用数据)。ViewData<T>这对于存储 HTML 页面标题和与母版页共享数据很有用。
  • 使用完全评估和加载的EF 模型作为View<TModel>模型。

但是请谨慎使用这种方法,因为它会引入不一致。

于 2013-01-20T09:13:52.447 回答
4

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

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

模型、视图和控制器之间的依赖关系越少,就越容易在不破坏视图和控制器等接口合同的情况下对 DomainModel 进行更改。但再一次,它是务实的。我喜欢这种方法,因为代码重构是系统维护的重要组成部分——重构可能包括对模型属性的简单拼写错误——如果不分离依赖关系,更改可能会通过代码波及到合同级别;例如。

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

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

[任何事物的] 良好设计的一个属性是能够替换或修改实现的一部分,而对系统的其余部分几乎没有影响。但这需要付出努力和时间来实现——您可以在完美设计和恰到好处的设计之间找到实际的平衡

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

没有什么会强迫您使用 ViewModels... ASP.NET MVC 不会强迫您。听取你内心的实用主义者的建议。

于 2013-01-20T09:24:58.493 回答
3

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

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

或使用 nuget Install-Package AutoMapper 安装

于 2013-01-21T07:52:09.500 回答
1

在我看来,对于执行大部分 CRUD 操作的复杂应用程序来说,在模型层之上再增加一层(ViewModel)是必不可少的,因为它具有以下优点:

  1. To establish loose coupling between Model and Controller. So that any DataModel related modifications will not be affected to Controller.
  2. If you've implemented your application's ViewModel layer correctly by providing maximum level of IOC(Inversion of Control) via DI(dependency Injection using Unity/other frameworks), etc., it will also help you to MOQ your ViewModels(dependencies) for testing only the controller's logic.
于 2017-03-07T11:30:57.287 回答