ASP.NET MVC – 数据库实体或ViewModels?

我目前正在研究一个ASP.NET MVC项目。

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

其他开发人员希望创build量身定制的ViewModel并将其绑定到视图。

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

(通过“数据库实体”我指的是ORM框架生成的自动生成的类,如LINQ to SQL,entity framework或LLBLGen)。

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

缺点:

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

优点:

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

正统的做法是,你不应该在你的视图中使用你的原始数据库实体。 像任何规则一样,如果你了解自己的实体,并理解其后果,就会被打破,但是有很好的理由不要违反这个规则,特别是在团队中工作时,以及将来人们将要维护的代码他们可能不了解规则或实体,就像你一样。 主要原因是:

  1. ORM懒加载。 设想你的客户有一个懒加载的集合订单。 您将Customer传递给View,并通过Orders进行迭代。 您在订单表上得到N * 1select。 但是这也意味着你的数据库连接仍然需要在View中打开。 有一种模式,人们使用“Transaction per Action”来处理Action_Executed事件中的数据库上下文,这种情况发生在View之前。 所以你可能试图访问数据库后,它已被处置。 即使你现在没有这样做,以后有人可能会决定实施这个模式,因为它很时尚。

  2. ViewModel的问题与db模型不同。 例如,你通常使用validation属性修饰你的ViewModel属性。 这些通常是不同的或者只涉及UI而不是db。 如果绑定到数据库实体,则会发现所有这些UI问题都会污染DB实体。

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

  4. 你可以单独testing你的ViewModel与数据库隔离。 ViewModels最终可能包含很多需要进行unit testing的逻辑。 如果它不绑定到数据库(如EF实体),则更容易进行testing。

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

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

使用视图模型:

优点:

  • 视图模型只包含视图所需的属性,没有别的。
  • 视图模型可能包含使用数据注释或IDataErrorInfo的特定validation规则。
  • 视图模型可以组合来自不同数据库实体的值。
  • 查看模型文件自己,并没有绑定到任何框架。
  • 视图模型可以保护您免受包含值的伪造POST,这些值在表单中未提供,但包含在ORM实体中。
  • 您可以轻松地指定视图模型的显示模板,并使用DisplayForEditorFor帮助器在很多地方重复使用它们。

使用ORM实体:

缺点:

  • ORM实体已经包含数据注释,这可能会弄乱你的validation。 例如:用户的密码字段可能会被标记为Required ,但是当您只更改基本的用户信息时不需要。
  • ORM实体与框架(Entity Framework)紧密相关,并且可能不容易在其中实现规则。
  • ORM实体可以包含多个视图的属性,但很难将不同视图的validation规则分开。
  • 使用带有延迟加载的ORM实体可以使您在呈现视图时执行SQL查询。 它不应该发生。
  • 使用ORM实体可能导致使用巨大的SQL查询,而不是小的。 当你想显示带有名字和姓氏的下拉菜单时,你只能从数据库中检索名字和姓氏,而不是整个实体。

感谢迄今为止的答案 – 这对于理解这两种方法的利弊大有帮助。 我有一件事要补充,没有人提到。

过度发布攻击

直接绑定数据库实体的一个令人担忧的缺点是“超额发布攻击”。 这是攻击者使用比FireBug更高级的工具可以插入的表单字段,不能被用户编辑,但是存在于DB实体上。

考虑一个“编辑我的个人资料”页面。 您的观点可能如下所示:

 @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" /> } 

它会呈现以下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> 

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

  <input type="hidden" name="IsAdmin" value="true" /> 

…突然间用户能够以非常意想不到的方式改变数据。

这里有一些更可怕的隐藏表单字段:

  <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" /> 

哎哟!

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

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

我还发现,视图模型有助于保存页面上所需的所有相关数据。 有些开发人员喜欢使用dynamicViewBag,但这对testing和debugging是不利的。

特别是,当您想从下拉列表中select关联的实体时,视图模型变得很容易。

AutoMapper是这个项目的救星,因为它省下了大量的映射代码,我只需要创build视图模型,然后从实体自动映射到控制器来查看模型。

不要将后端实体暴露给客户端。 真实世界的应用程序有行为 – 不CRUD。 你把你的实体数据绑定到视图上,在客户端需要行为时,只需要一点时间就可以挖掘到泥泞的黑客。

我只是想添加与hackedbychinese完全一样的情绪。 另外我会添加fk的查找列表,你只需要使用viewmodels作为实体模型只会持有一个指针,在该表中的一个单一的ID。 视图模型允许您将所需的填充列表传递到视图 – 瞧。

另外,视图模型在需要时可以包含谨慎的逻辑,这实际上不是实体模型的情况。 此外,您的validation可能会根据您的视图的使用而有所不同,因此可以根据“查看”要求应用不同的validation。

ViewModel的目的主要是分离关注点 – 将View与Model的实现细节分开。

在视图中使用数据库实体,特别是表单是一个巨大的安全问题。 以下面的POCO对象

 public class User { public int Id { get; set; } public string Username { get; set; } public string Email { get; set; } public bool IsAdmin { get; set; } } 

现在说你正在呈现一个允许用户更改电子邮件的视图。 当使用Db实体而不是视图模型时,处理表单结果的MVC方法看起来像:(除非你不使用模型绑定,在这种情况下,你正在为自己做更多的工作)

 public class HomeController : Controller { [HttpPost] public ActionResult ChangeEmail(User user) { //.... } } 

在Asp.net中的模型绑定通过查找匹配模型中属性名称的GET或POST参数来工作。 因此,所有用户所要做的就是将IsAdmin=true添加到POSt参数和viola中,传入ChangeEmail函数的模型将IsAdmin属性设置为true,这很容易被无意中添加到数据库中,从而为用户提供了自由访问更改他们没有访问权限的数据。

这是为了用户权限,改变谁拥有一个实体(使你的问题与我相关而不是你),改变原创的date等…