8

我试图了解整个 MVC/EF 关系。如果我创建一个仅与数据库交互的实体模型(因为您不应该将实体模型传递给视图),然后是模型的类,最后是视图模型,如下所示。我唯一的问题是拥有第二个类似乎是多余的,我所看到的示例中唯一的不同是它们将数据注释应用于该类,因为它与视图交互。为什么确保实体对象不暴露在视图层如此重要?

我还没有真正开始编写项目,但我假设您将使用实体模型与数据库进行交互,然后将其转换为 ProductModel 以传递给视图这是正确的逻辑吗?

实体模型:

public class Product 
{
    [Key()]
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public double Price { get; set; }
}

模型:

public class ProductModel
{
    public int ID { get; set; }
    [StringLength(50)]
    [Required(ErrorMessage = "Product Name is required.")]
    [Display(Name = "Product Name")]
    public string Name { get; set; }
    public string Description { get; set; }
    public double Price { get; set; }
}

视图模型:

public class ProductViewModel
{
    Product myProduct { get; set; }\
    //Plus any other properties I may need for the view.
}

更新:

在我一直在阅读的示例中,他们还设置了如下 DBContext。那么 ProductModel 类就没有用了吗?

public class MyAppContext : DbContext
{
    public MyAppContext()
        : base("name=DBConnection")
    { 
    }

    public DbSet<Product> Products { get; set; }

 }
4

7 回答 7

3

有时,尤其是在简单模型上,可能不需要视图模型。但是,一个很大的“但是”,我发现这种情况的实例很少,即使那样,我后来通常还是需要返回并创建一个视图模型,无论如何。拥有一个专门的视图模型更安全、更简单、更容易。

您可能会将其视为额外的工作,但可以从关注点分离的角度来考虑它(这是 MVC 的全部要点)。例如,如果我想为输入显示一个 SelectList,我可以将它添加到 ViewBag 或我的模型中。如果我将它添加到 ViewBag,我会失去强类型,这绝不是理想的,但它也不属于我的数据库跟踪实体。拥有一个视图模型,让我把这些信息准确地放在它应该去的地方:一个强类型模型,它的存在是为了服务于视图并且只服务于视图。

或者,考虑验证:如果我想要一个数据库所需的字段(非空)怎么办,但我想让这个对用户来说是可选的,如果用户选择不指定,我自己在后台用一些业务逻辑填充它. 视图模型可以轻松处理该抽象,而将其添加到实体本身会增加巨大的复杂性。

当然,什么都不是必需的。您始终可以随心所欲地设置您的项目,但最佳实践之所以成为最佳实践是有原因的:就像您一样的开发人员一次又一次地遇到相同的问题并围绕一个可行的解决方案进行合并。您可能可以暂时避免使用视图模型,但最终,您会遇到相同的障碍并无论如何都要合并它们,所以从一开始就这样做,让您的生活更轻松。

于 2013-03-28T19:17:31.507 回答
1

ViewModel 将是实际传递给浏览器/从浏览器传递的内容,如果您正在构建可以就地更新/就地保存的东西,通常通过 JSON。所以:

  • ASP.Net MVC 在它可以通过 JSON 生成/使用的内容方面存在一些限制(并且其限制在每个方向上略有不同);您可以使用 ViewModel 来解决这个问题

  • 您从数据库中拉回的数据大小可能不是需要到达浏览器的 - 例如,您可能需要拉回一些额外的字段并在传递之前检查它们,但只想传递一个子集 - ViewModel 是子集。

  • JSON 中的一些自然结构在数据库中并不真正可用 - 例如,您可能在模型中存储了相当于 Dictionary 的内容,例如一个带有一些值的表,另一个带有 FK 指向它的表,一个 Id , 和一个字符串值 - 但要让浏览器利用它,您可能只需要该字符串值。因此,在 ViewModel 中,您使用一个简单的 Dictionary 来表示所有内容(最终在客户端上作为一个简单的 JS 对象)。

  • 通常,诸如日期格式之类的东西在客户端上很弱,或者由于客户端具有准确的系统时钟等而变得脆弱。我经常在我的 ViewModel 中使用一个字符串,我的模型中有一个 DateTime,并将 UTC 转换为他们的时区和格式在它到达他们的浏览器之前很好地在服务器上。

  • 有时您需要避免将模型的某些部分暴露给浏览器;例如,在某些系统中,如果您将行 ID 暴露给浏览器,您可能会产生安全风险。ViewModel 使隐藏模型的部分变得微不足道。

另请参阅:如何设计 ViewModel

于 2013-03-28T23:54:02.007 回答
1

我创建一个与我的实体分开的模型类有两个主要原因。

  1. 正如你提到的,属性。您可能希望在多个应用程序中重用您的实体,它们可能不会使用相同的属性。你不想用这些污染你的实体。

  2. 根据它们的 ORM,您的实体可能需要一个基类。或者,您可能需要将属性或其他自定义应用于实体。这可能会导致在测试业务逻辑时出现困难。此外,如果您更改 ORM 或 ORM 更改中的某些内容,您将保持该更改与应用程序的其余部分隔离。

基本上,您是在隔离应用程序的不同层,并保护一层免受另一层的更改。

于 2013-03-28T18:26:28.433 回答
1

你需要Product classProductViewModel class然后你的DbContext. 如果您是第一次这样做,请阅读Pro ASP.NET MVC 3 Framework,第三版

或者

临 Asp.Net Mvc 4

他们有关于 MVC 的详细信息,两本书都有一个Real Application tutorial you can follow从头到尾的内容,包括部署。

您还将了解单元测试和其他 MVC 工具,例如依赖注入 (Ninject) 和 Moq

于 2013-03-28T18:28:21.567 回答
1

除了上面的答案之外,还有一点没有提到,以防止将数据发送到不需要的视图/客户端。

例如,假设您的产品型号包含您向供应商支付的购买产品的价格。您不希望您的客户看到此数据,但如果它包含在发送到视图的模型中 - 即使您不显示该字段 - 他们也可以评估它。在这种情况下,您将使用不同的视图模型并将数据从 ef/数据库模型复制到视图模型,然后再将其发送到视图。

有时您可能会得到一个 DBcontext 类、一个 EF/数据库类/模型和几个视图模型,每个视图模型都包含来自数据库模型的不同数据子集。

您还可以发现自己的视图模型包含来自多个数据库模型的数据,您经常会看到视图使用列表或下拉列表作为在视图包中发送列表选项的替代方案。

于 2013-03-28T18:39:04.597 回答
0

首先,有一些缺失和组合的技术,一种是从所有其他层完全抽象 DAL,这是一种技术。但是还有其他可以使用的技术;使用“实体”类作为域类。在一个简单的场景中,我们总是使用业务层上的域类来应用所有业务规则。这将有助于我们在不增加大量代码行/类数量的情况下通过层来组合可测试性并避免层之间的无用链接。

当您使用 MVC 时,这种在所有层中拥有此域对象(域类)的方法也会使您的事情变得更容易,因为这些类可以具有数据注释,供以下人员使用:

  1. 验证视图
  2. 完整性数据库

此外,要了解此类类的概念和使用,我们需要注意一些事情。如果我们使用 POCO 作为我们的实体和域类,那么这些类与 Entity Framework 在解释对 DB 的查询时将使用的类不同。相反,EF 将创建将域对象表示为实体的动态类(从 POCO 派生)并加载所有虚拟字段,通常是相关实体。

而且您将保存类代码和简单的重新映射。

希望这可以帮助

于 2013-03-28T23:33:24.117 回答
-1

为什么确保实体对象不暴露在视图层如此重要?

它不是。对于一个简单的 CRUD 控制器,通常更容易只传入实体对象。对于更复杂的页面,您可能会同时与多个实体对象/类型进行交互。您将如何在不创建新模型类的情况下传递有关这两个对象的信息?

于 2013-03-28T18:37:56.527 回答