ASP.NET MVC - 数据库实体还是ViewModels?

Ale*_*ork 39 asp.net-mvc orm viewmodel asp.net-mvc-3

我目前正在开发一个ASP.NET MVC项目.

团队中的一些开发人员希望将自动生成的数据库实体直接绑定到视图.

其他开发人员希望创建量身定制的ViewModel并将其绑定到Views.

客观地说,这两种方法的优点和缺点是什么?

("数据库实体"我指的是ORM框架生成的自动生成的类,例如LINQ to SQL,Entity Framework或LLBLGen).

Hac*_*ese 50

绝对在视图中使用视图模型,并使用类似的东西AutoMapper轻松地从实体创建视图模型.

缺点:

  1. 有时,感觉就像是在复制代码,特别是当视图模型和实体具有完全相同的属性时

优点:

  1. 您经常需要以更简单的格式表示对象(通常称为展平),但您需要在服务器端完全保真.这允许您在两者之间进行转换,而不会通过演示文稿来破坏您的域模型.
  2. 聚合根通常具有许多值对象和与特定视图无关的其他实体,并且在视图模型中省略它们使得它更易于使用.
  3. 您的实体将有许多双向引用,这些引用在API方面是合理的,但在为JSON,XML等序列化它们时创建纯粹的地狱.视图模型将消除这些循环引用.
  4. 您可能经常使用相同的实体,但以不同的方式使用不同的视图.试图在一种类型上平衡两种需求可能会造成巨大的混乱.


Rob*_*ent 16

正统观点是您不应该在视图中使用原始数据库实体.像任何规则一样,如果您了解自己的实体并且了解后果,那么它就会被打破,但是有很好的理由不打破这个规则,特别是在团队合作和将来会保留的代码时谁也可能不了解规则或实体.主要原因是:

  1. ORM延迟加载.想象一下,您的客户有一个延迟加载的订单.您将Customer传递给View,它会遍历Orders.您在订单表上获得N*1选择.但这也意味着您的数据库连接仍需要在View中打开.有一种模式,人们使用'每个操作事务'来处理Action_Executed事件中的数据库上下文,该事件发生在View之前.因此,您可能会在处理完数据库后尝试访问该数据库.即使你现在没有这样做,未来的某个人可能决定实施这种模式,因为它很时髦.

  2. ViewModel的关注点与db Model不同.例如,您通常使用验证属性装饰ViewModel属性.这些通常是不同的或仅涉及UI而不是数据库.如果绑定到数据库实体,您将发现所有这些UI问题都会污染数据库实体.

  3. 与2相关 - ViewModel的要求可能需要计算或派生属性.例如,使用First和Last名称构造的Fullname.这些东西最好保存在ViewModel中.

  4. 您可以独立于数据库对ViewModel进行单元测试.ViewModels最终可能包含很多需要进行单元测试的逻辑.如果它不依赖于您的数据库(与EF实体一样),则更容易测试.

一般来说,创建和维护ViewModel(即使没有AutoMapper)也不是一个开销,你会发现它是一个更好的开发模式.除了最简单的情况(例如,静态数据的查找列表)之外,我会推荐它.


Luk*_*Led 12

我相信使用视图模型是唯一的方法,所以没有ORM实体的优点:)视图模型不仅提供视图数据,而且还定义视图应该如何(通过定义模板)或如何验证(通过添加数据注释或实现IDataErrorInfo).

使用视图模型:

优点:

  • 视图模型仅包含视图所需的属性,没有其他内容.
  • 视图模型可能包含使用数据注释或IDataErrorInfo的特定验证规则.
  • 视图模型可以组合来自不同数据库实体的值.
  • 查看模型文档本身并不依赖于任何框架.
  • View模型可以保护您免受伪造的POST的影响,这些POST包含未在表单中提供但包含在ORM实体中的值.
  • 您可以为视图模型轻松指定显示模板,并使用DisplayForEditorFor帮助程序在许多地方重复使用它们.

使用ORM实体:

缺点:

  • ORM实体已经包含数据注释,这可能会破坏您的验证.示例:用户中的密码字段可能标记为Required,但仅在更改基本用户信息时不需要.
  • ORM实体与Framework(实体框架)密切相关,可能不容易实现规则.
  • ORM实体可以包含多个视图的属性,但很难为不同的视图分隔验证规则.
  • 使用延迟加载的ORM实体可以使您在呈现视图时执行SQL查询.它不应该发生.
  • 使用ORM实体可以导致使用大型SQL查询而不是小型SQL查询.如果要显示带有名字和姓氏的下拉列表,则只应从数据库中检索名字和姓氏,而不是整个实体.


Ale*_*ork 8

感谢到目前为止的答案 - 它们对理解两种方法的优缺点有很大帮助.我有一件事要补充一点,没有人提到过.

过度发布攻击

直接绑定数据库实体的一个令人担忧的缺点是"过度发布攻击".这是攻击者使用不高于FireBug的工具,可以插入不打算由用户编辑但在数据库实体上存在的表单字段.

考虑"编辑我的个人资料"页面.您的视图可能如下所示:

@using(Html.BeginForm() {
  <div>
    @Html.LabelFor(x=> x.FirstName)
    @Html.TextBoxFor(x=> x.FirstName)
  </div>
  <div>
    @Html.LabelFor(x=> x.LastName)
    @Html.TextBoxFor(x=> x.LastName)
  </div>

  <input type="Submit" value="Save" />
}
Run Code Online (Sandbox Code Playgroud)

它将呈现以下HTML:

<form action="/profile/edit" method="post">
  <div>
    <label for="FirstName">FirstName</label>
    <input type="text" name="FirstName" value="" />
  </div>
  <div>
    <label for="LastName">LastName</label>
    <input type="text" name="LastName" value="" />
  </div>

  <input type="Submit" value="Save" />
</form>
Run Code Online (Sandbox Code Playgroud)

使用FireBug,攻击者只需要在表单中插入一大块HTML:

  <input type="hidden" name="IsAdmin" value="true" />
Run Code Online (Sandbox Code Playgroud)

......突然之间,用户可以以非常意外和有害的方式更改数据.

这里有一些更可怕的隐藏形式字段:

  <input type="hidden" name="ShoppingCart.Items[0].Price" value="0.01" />
  <input type="hidden" name="BankAccount.Balance" value="1000000" />
  <input type="hidden" name="User.Administrator.Password" value="hackedPassword" />
Run Code Online (Sandbox Code Playgroud)

哎哟!

信息摘自:http: //hendryluk.wordpress.com/tag/asp-net-mvc/


gls*_*123 5

我曾经尝试开发一个直接在ASP.NET视图中使用NHibernate实体的应用程序.我遇到了许多问题,Lazy加载和延迟SQL执行直接从视图运行,而不是在业务逻辑层甚至控制器中运行.转移到视图模型并使用automapper似乎解决了所有这些问题,使应用程序更容易测试,调试和维护.

我还发现视图模型有助于保存页面上所需的所有相关数据.一些开发人员喜欢使用动态ViewBag,但这对测试和调试很不利.

特别是,当您想从下拉列表中选择关联实体时,视图模型可以轻松实现.

AutoMapper是这个项目的救星,因为它节省了编写大量的映射代码,我所要做的就是创建视图模型,然后控制器从实体自动化到查看模型.