ASP.NET MVC - 视图模型模式

3
我只在MVC框架(ASP.NET MVC2/3/Razor)上工作了几个月,我很喜欢它,但是我很难找到View Models的标准。在我的项目中,我有一个Models文件夹(包含我的数据模型-Linq DBML、Repository[ies]、扩展方法)和一个Models/ViewModels文件夹。我的视图模型通常是可重用的类,通常只包含LINQ对象或我需要访问特定视图的对象集合的简单get/set属性。
现在我遇到的问题是如何确定何时创建视图模型。我的目标是尽可能经常使用LINQ对象作为视图模型,尤其是在编辑操作时。我的问题是,如果我有其他的数据片段仅供显示使用怎么办?我不喜欢使用ViewData/ViewBag集合,因为访问这些成员需要知道集合项的键(对于设计师/前端人员来说并不容易“猜测”)。我也不喜欢为每个视图创建一个ViewModel,因为它看起来像是不必要的混乱代码。
例如,假设我有一个员工的数据模型,并且我想显示与该员工无关的一些信息-比如,站点统计信息、动态菜单以及您可以想到的其他可能来自数据库的内容。我应该将哪个模型传递给/Employee/Edit操作?带有大量ViewData[]的Employee对象还是自定义的EmployeeView?
是否有一个黄金标准?我错过了什么?你正在做什么不同的事情,我应该研究一下?提前致谢!
3个回答

3

我从不直接使用实体类作为视图模型。我总是为每个视图创建一个专用的模型,其中包含该视图的数据。我开始使用AutoMapper在视图模型和实体模型之间进行映射。我通过一个ModelMapper类来协调这个过程,它有一个ToViewModel<TEntity,TViewModel>()方法来处理标准映射(只需调用自动映射器)和特殊映射方法来支持从视图模型创建和更新实体。


你的视图模型是什么样子的?我见过各种各样的视图模型,从几乎与它们所代表的LINQ对象相同的副本到具有大量业务逻辑方法的复杂类(这似乎对于视图模型来说是不可取的)。我的视图模型就像上面描述的那样,是简单的类,具有用于我的LINQ数据对象的get/set属性。 - Keith
1
@Keith - 通常是简单的容器,但我进行模型级别的验证,因此属性会被装饰以适当的属性(实体模型也是如此)。业务逻辑(除了基于属性的验证之外)被隔离在模型映射类和存储库基础设施中。此外,我还有一个规范提供程序用于查询规范,实现业务级别的访问策略等。 - tvanfosson

2

你提到了三件事:

  • 网站统计
  • 动态菜单
  • 可能来自数据库的其他内容

第三点是个陷阱;仅仅因为某些东西在你的数据库里,并不意味着它是你领域模型的一部分。

导航可能基于数据库。它也可能在站点地图或应用程序中硬编码。实际上,它并不是应用程序数据,而是应用程序配置。如果你将其存储在应用程序数据库中,那么可以(好吧,其实不行),但它不是模型本身的一部分。

同样地,网站统计通常存储在数据库中,但它们存储在一个不同的数据库中,具体来说是分析数据库(许多人只是将其“外包”给 Google)。所以,它们是一种数据,但它们不是你的模型。

如果你想让你的应用程序有意义,你需要在概念层面上分离这些问题。导航在你的主页面/布局中完成,包括任何必要的动态代码以使其正常工作。它是纯视图逻辑-不要让它泄漏到你的模型中。对于与当前使用的实际功能无关的问题,完全可以使用 ViewData/ViewBag。

现在,假设还有其他类型的视图数据实际上是应用程序数据:原则上,视图应该直接连接到模型-毕竟这就是“MVC”的含义-但在实践中,它只是一个不明智的“规范数据模型”思想的重新实现。领域和表示是独立的问题,因此意味着不同的上下文模型-在后一种情况下,那是一个视图模型。

当我开始做MVC工作时,我也不愿使用视图模型。但是在我更习惯这个想法之后,我意识到这真的是唯一可行的解决方案-特别是当你的“模型”大多是对数据库本身的薄包装时。视图和数据以不同的方式和速度变化;如果你不想担心不断涌现的错误和回归,那么你需要在两者之间有些绝缘。构建一个映射层并结束吧。

此时,我已经开始为每个视图创建不同的视图模型,无论我是否认为需要它。在开始时,它可能只是一个模型类的副本,但这意味着我可以随时以任何方式进行调整,而不必与底层模型混淆 - 反之亦然。正如tv所说,最初的视图模型使用AutoMapper轻松生成,并且只需要大约30秒钟的时间。

只需使用视图模型,并希望最终工具支持视图模型自动生成即可。


虽然我理解关注点分离的重要性,但我喜欢调用UpdateModel()的简单性。为什么要添加另一层AutoMapper(说实话,我还没有看过)如果它似乎是不必要的呢?我同意,如果视图模型是MVC框架的重要组成部分,为什么它们不作为工具/向导的一部分包含在内呢?对我来说,这里有些不对劲。 - Keith
@Keith:首先,AutoMapper不是一个“层”,它是一个基于约定的映射器,几乎消除了你原本需要编写的所有映射代码。其次,在最开始时它似乎是不必要的;但不可避免地很快就会变得必要。最后但并非最不重要的一点是,“View”这个术语往往被过于字面化,就像“Model”一样——视图模型是视图的一部分,就像存储库是模型的一部分。事实上,大多数良好的架构都是这样设计的;每个层或组件都有自己的上下文模型,当你想从一个跳到另一个时,你进行映射。 - Aaronaught
@Keith:至于“我喜欢调用UpdateModel()的简单性”- 你仍然可以这样做,只需使用参数化方法重载 - Aaronaught
如果我的视图模型中有两个不同的模型(假设为model1和model2),那么我该如何使用哪个存储库来加载视图模型?是model1的存储库还是model2的存储库? - Foyzul Karim

0
如果您正在创建一个用于编辑员工的页面,但同时也需要显示一些其他内容(例如网站统计/菜单),为什么不将它们放在局部视图或布局文件中呢?

使用部分视图,您仍然需要传递一个模型来动态显示数据。 - Keith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接