65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 的“为什么”与“怎么做”:第二部分

2013年7月14日

CPOL

11分钟阅读

viewsIcon

91879

ASP.NET MVC 模型绑定方法、基本思想、优点以及模型验证。

引言

我知道距离我写完第一部分已经有一段时间了。您现在可能已经阅读了很多关于 ASP.NET MVC 的资源。因此,我现在将跳过“怎么做”部分,而将更多地关注“为什么”。此外,在本系列文章中,我将演示一些 ASP.NET MVC 中提供的新特性。请记住,在这篇文章中,我提到的 MVC 都指的是 MVC 4。在本文中,我们将仅限于讨论模型。为什么使用模型、处理模型时应采取什么方法以及如何验证模型。

ASP.NET 上的 MVC  

听起来有点过时且与主题无关,但即使现在,我仍然看到许多关于 Web 窗体开发者如何计划迁移到 MVC 的讨论。这是一个热门话题,因此我想分享一些这方面的见解。对于新开发者来说,MVC 很有吸引力,但对于那些考虑从 Web 窗体迁移到 MVC 的开发者来说,主要 concerns 是熟悉他们多年来习惯的功能。大多数 Web 窗体开发者的 concerns 是 ASP.NET Web 窗体框架在以下方面提供了很多功能:

  1. 安全性 
  2. 调试 
  3. 验证 
  4. 状态管理 
  5. 主母版页/用户控件 
  6. RAD 控件 
  7. 熟悉的语法 

现在,常见的问题是 ASP.NET MVC 是否能满足这些需求?它能做到吗?

嗯,让我们看看 ASP.NET MVC 提供了什么。尽管它们的架构方法非常不同,但 ASP.NET MVC 和 Web 窗体实际上有很多共同之处。

关注点 ASP.NET MVC Web Form
安全 窗体/Windows 身份验证、Membership 窗体/Windows 身份验证、Membership
验证 数据注解 用于验证的服务器控件
状态管理 会话和配置文件状态 视图状态/Cookie/查询字符串
跨页面数据传输 从控制器到视图/从控制器到请求的 ViewData、ViewBag 和 TempData 会话/查询字符串
健康监控 健康监控 健康监控
缓存 输出和数据缓存 输出和数据缓存
主页模板 布局页 (Layout Page) 主页模板
用户控件 部分视图 用户控件
RAD 控件 HTML 助手 服务器控件
语法 Razor 视图引擎 ASPX 视图引擎

Bingo,正如您所见,一个准备学习 ASP.NET MVC 的 Web 窗体开发者比他想象的已经更进一步了。在生产力方面,ASP.NET Web 窗体和 MVC 没有太大区别,我们可以当然可以提出 RAD 控件的问题。我同意 RAD 控件可以加快开发速度,但想想 UX 设计师如何在没有开发人员参与的情况下玩转视图。开发人员不必花费精力使用 RAD 控件来实现酷炫的 UX。结合这些最终的开发工作,Web 窗体和 MVC 之间的差异非常小。此外,如今有许多优秀的框架,如 AngularJs/Backbone js,可以简化开发工作。生产力是希望从 Web 窗体迁移到 MVC 的开发者的主要 concerns 之一。

这并不奇怪,就像 Web 窗体一样,ASP.NET MVC 是建立在 ASP.NET 平台之上的。因此,这两个框架都依赖 .NET 语言 C# 和 Visual Basic .NET 与 .NET Framework 进行交互,并且可以访问使用 .NET Framework 支持的任何其他语言开发的已编译程序集。更有趣的是,在开发 MVC 时,您会看到非常熟悉的 *web.config* 和 *Global.asax*。如果您在 Google 上搜索,您可能会找到成千上万关于这些比较的资源。

使用模型

现在您可能会问一个显而易见的问题:数据库优先、模型优先还是代码优先?这些都是什么,它们与 MVC 有什么关系?说得好,但要回答这个问题,让我们先退一步,回顾一下第一部分,其中讨论了 **M**VC 的模型部分。让我们来回忆一下。

模型是 MVC 应用程序中与应用程序数据域交互并从数据库检索/存储模型状态的部分。

所以模型是您应用程序的核心(从业务角度来看),它处理最重要的元素:“数据”。模型代表您的数据结构,并将包含帮助您执行 CRUD(创建/检索/更新/删除)操作的函数。现在,根据架构、应用程序类型/范围、风险分析,我们需要不同的方法来实现模型实体。这就是 EF(Entity Framework)发挥作用的地方。使用 EF,我们可以灵活地按需处理模型。

Entity Framework 提供了三种定义实体模型的方法:使用数据库优先工作流、模型优先工作流,以及最后但同样重要的是,代码优先方法。由于对正在编写或实现的类拥有完全控制权,代码优先的模型设计方法正成为一种流行的选择。但这并不意味着数据库优先或模型优先不适合使用。诀窍在于知道何时使用何种方法;如果您能够分析您的应用程序场景/范围/风险并选择最适合您场景的方法,在我看来,这才是酷炫的。让我们深入到我们的主要话题。

为什么是数据库优先

考虑这样一个场景:一个现有的企业数据库有许多表,每个表都有许多列。它们没有您喜欢的规范化,但您也无话可说,因为这是一个现有的数据库。当业务正在进行时,没有人会允许您破坏和重构数据库。在这种情况下,您的应用程序的实体模型必须与现有数据库兼容;您不能弯曲数据库来适应您偏好的对象模型,换句话说,您的双手被束缚了。数据库优先在这种场景中很受欢迎,也称为“遗留系统”场景,适用于那些构建应用程序访问现有生产数据库(包含大量表和列)的开发人员。

何时采用数据库优先方法,在论坛、群组、用户聚会等地方有不同的看法。我不想重复造轮子,我喜欢总结它们,这里有一个列表:基于 DBA 设计的现有数据库构建的遗留系统,独立开发,或者如果您有现有数据库。

  1. 您会让 EF 为您创建实体,在修改映射后,您将生成 POCO 实体。
  2. 如果您希望 POCO 实体具有其他功能,您必须修改 T4 模板或使用部分类。
  3. 数据库定义您的领域模型。
  4. 大量的业务逻辑驻留在数据库端,以实现更好的可维护性。

现在的问题是,使用数据库优先是否不好?许多专家有自己的观点,我的观点是,尽管对象模型不尽如人意,但如果应用程序按预期运行,那么就没有理由对此不满意。我们在这里讨论的每种方法都有其优点和缺点。

优点

  1. RAD 开发方法,EF 将包含现有数据库的所有常用元数据、CSDL、SSDL 以及它们之间的映射,就像模型优先一样。
  2. 在处理大量数据时,可以首先专注于数据库设计。
  3. 数据库不仅仅是存储,还可以用于实现业务逻辑。

缺点

  1. 对 EF 生成的代码没有控制权。
  2. EF 生成的代码太复杂,无法自定义。
  3. 对 EF 生成的实体控制较少。
  4. 强制执行自定义实体级验证需要 T4 模板的专业知识。

为什么是模型优先

尽管数据库优先方法是后续方法,但不断变化的开发场景要求进行领域驱动开发,模型优先带来了 DDD 的精髓,但并非真正如此。模型优先设计开始流行,其中数据库模式基于模型。尽管概念上数据库优先和模型优先方法不同,但从实现的角度来看,差别不大。两者最终都会生成大量自动生成的代码。

与数据库优先一样,何时采用此方法有各种各样的答案,我的总结版本是:

  1. 在我看来,如果您是设计者粉丝(=您不喜欢编写代码或 SQL),它很受欢迎。
  2. 您将“绘制”您的模型,然后让工作流生成您的数据库脚本,T4 模板生成您的 POCO 实体。您将失去对实体和数据库的部分控制,但对于小型简单项目,您将非常高效。
  3. 如果您希望 POCO 实体具有其他功能,您必须修改 T4 模板或使用部分类。
  4. 对数据库的手动更改很可能会丢失,因为您的模型定义了数据库。如果您安装了数据库生成包,这会更好。它将允许您更新数据库架构(而不是重新创建)或更新 VS 中的数据库项目。

优点

  1. 数据库不仅仅是存储,还可以用于实现业务逻辑。
  2. 创建符合您的业务域的模型及其关系的灵活性。
  3. 最小化由生成的对象模型(代码)或数据库中的更改引起的“涟漪”效应。

缺点

  1. 每次创建数据库时,数据库的内容都会被重新生成,导致数据丢失,因为它每次都从模型重新生成一切。
  2. 一旦数据库架构发布,许多情况下可以直接修改数据库,因此模型会过时,并且必须以数据库优先的方式从数据库进行更新。

为什么是代码优先

在代码优先方法中,您完全避免使用 Visual Model Designer (EDMX)。您首先编写 POCO 类,然后根据这些 POCO 类创建数据库。这是领域驱动设计 (DDD) 的理想选择。代码优先使用类和属性分别标识表和列。代码优先有许多优点。首先,开发人员不再因为没有数据库而延迟。您现在可以创建数据库结构并开始编码,所有内容都保留在同一个解决方案中。其次,没有自动生成的代码;开发人员可以完全控制每个类。最后,数据访问层中的所有内容都保持简单,因为没有需要更新或维护的 *.EDMX* 模型。

优点 

  1. 易于集成模型验证。
  2. EF modeling 经常会出现一些奇怪的错误,例如当您尝试重命名关联属性时,它需要与底层元数据匹配 - 非常不灵活。
  3. 回到代码优先,您拥有完全的控制权,无需处理所有隐藏的复杂性和未知数。
  4. 启用迁移可以跟踪架构更改,因此可以在模型更改的同时无缝部署升级/降级。

我是代码优先的粉丝,所以我猜我对缺点的看法是偏颇的。

缺点

  1. 不支持可视化模型设计器 (EDMX)。
  2. 无自动生成代码。
  3. 对数据库的手动更改很可能会丢失,因为您的代码定义了数据库。

关于何时使用代码优先没有硬性规定,但总的来说,您可以使用代码优先,因为:

  1. 完全控制代码(没有难以修改的自动生成代码)。
  2. 非常适合具有复杂业务需求的应用。
  3. 非常受欢迎,因为硬核程序员不喜欢设计器,并且在 EDMX XML 中定义映射太复杂。
  4. 如果您期望数据库在您的应用程序中的作用仅仅是存储。EF 将处理创建,而您不想知道它是如何工作的。

模型验证 

正如我们在以上三种方法中讨论过的,模型对象处理数据并在该数据上执行业务逻辑。模型是应用程序特定的,因此 ASP.NET 框架对模型对象的构造没有限制。但是我们可以对模型强制执行自定义验证。添加此类验证最常见和最方便的方法是代码优先,因为这种方法确保了对模型的完全控制,并且有两种流行的验证方法:DataAnnotation 和 FluentValidation。DataAnnotation 和 FluentValidation 都服务于类似的目的。它们确保已分配给对象属性的值满足应适用的业务规则。

DataAnnotation FluentValidation  
内置于 .NET Framework 的验证支持,并且只需最少的代码即可集成。 高级验证支持,这是数据注释无法实现的。
不打算与 DB/Model First 一起使用,但可以通过一些变通方法(伙伴类)提供支持。 仅支持代码优先方法。

这是否意味着数据库优先或模型优先方法不需要任何模型验证?在使用数据库优先或模型优先时,EDMX 文件具有相同的目的——它包含模型的所有详细信息和映射定义。您定义的任何限制(或约束)都将通过 EDMX 映射定义转换为数据库/数据库模型,因此您无需额外努力来定义自己的限制。只有在使用代码优先时,Fluent API 或数据注释和约定才会取代 EDMX 文件。

测试模型

MVC 的一个主要关注点一直是测试。因此,本文在不讨论如何对模型进行单元测试的情况下是不完整的。只是提前告知,我将尝试展示正面和负面测试。希望将来能详细讨论。例如,我们以一个带有 DataAnnotation 的非常简单的类为例,并且只进行属性设置规则测试。供您参考,许多专家建议测试派生属性而不是测试每个属性。

public class User
{
    public string FName { private get; set; }

    public string LName { private get; set; }

    [Required]
    [StringLength(10)]
    public string Name { get { return string.Format("{0} {1}", this.FName, this.LName); } }

}  

现在,最后进行测试,为了简单起见,我只测试 Name 规则,我编写了两个测试:一个正面测试,一个负面测试。
正面测试:确保名称长度满足允许的长度

    [TestMethod]
    public void Name_Should_have_valid_length()
    {
        User _userToTest = new User( );
        _userToTest.LName = "Sh."; //3 character
        _userToTest.FName = "Iqbal"; //5 character
        Assert.IsTrue(_userToTest.Name.Length <= 10);

    } 
负面测试:确保在名称超出允许长度时抛出错误
    [TestMethod]
    public void Name_Should_Fail_for_invalid_length()
    {
        User _userToTest = new User();
        _userToTest.LName = "Shahriar"; //8 character
        _userToTest.FName = "Iqbal"; //5 character
        Assert.IsTrue(_userToTest.Name.Length <= 10);

    }
为了有效的测试,应同时利用正面和负面测试。我知道这个例子非常小且基础,但对初学者来说很好,我希望在接下来的文章中展示更多详细的测试技巧。

参考和好读

系列 

  1. ASP.NET MVC 的“为什么”与“怎么做”:第一部分 - MVC 基础。

历史

  • 版本 1.0。
© . All rights reserved.