使用 SQL Server 和 Entity Framework 设计 ASP.NET MVC 应用程序的初学者指南






4.94/5 (27投票s)
在本文中,我们将讨论如何使用 SqlServer 和 Entity Framework 设计 ASP.NET MVC 应用程序
引言
在本文中,我们将讨论如何使用 SQL Server 和 Entity Framework 设计 ASP.NET MVC 应用程序。
背景
上周末,我偶然拜访了一位朋友的工作场所。这伙人想在启动一个新项目时获得一些非官方的帮助。他们的公司多年来一直在开发基于 Web Forms 的应用程序。现在他们希望在新项目中切换到 ASP.NET MVC。组织中的每个人都非常兴奋,并且正在学习 ASP.NET MVC。但他们对如何用这项新技术启动项目有些担忧。像 n 层架构、数据访问和单元测试这样的问题应该如何规划和实施。我花了一整天的时间和他们在一起,大部分时间都在白板前,用投影仪展示 ASP.NET MVC 架构的图表和文本。一天结束时,我们制定了一个从头开始设计 ASP.NET MVC 应用程序的计划。这并不是一个完美的计划,但这是一个不错的计划。那天晚些时候,我想,为什么不把我的所有讨论都记录成一篇文章,以便其他人也能阅读这些信息,并可能觉得有用呢。
在本文中,我将假设读者对 ASP.NET MVC 有一些基本的了解和认识,并且有一些使用 ASP.NET Web Forms 的背景。我还会提供有用的文本链接,以便读者可以阅读它们以完全理解上下文。
使用代码
一个良好的应用程序架构最棒的事情之一就是关注点分离 (SoC)。ASP.NET MVC 应用程序的一个明显特征是能够开箱即用地提供这种关注点分离。模型将代表实现解决方案所需的业务对象。视图是用户可见的部分。用户可以简单地使用视图来消费数据,或者对数据执行操作,即 CRUD 操作。控制器是提供模型和视图之间交互机制的部分。控制器负责使用模型和视图来协调整个应用程序/演示。模型和视图保持松耦合,即模型不知道视图的任何信息,而视图有一个模型对象(关联)来提取信息并显示给用户。
注意:对于不知道 ASP.NET MVC 的人
选择数据层
对于任何业务应用程序,数据访问都是一个非常关键的部分。每当我们设计应用程序时,都应该考虑应用程序将使用的数据访问方法。要处理的第一个问题是我们将为应用程序使用哪种类型的数据存储。我们可以选择
- 像 SQL Server 这样的关系数据库
- 或者像 RavenDb 这样的 NoSql 数据库,它是一个基于文档的数据库。
如果我们选择 NoSql 数据库,那么我们的模型(即领域对象)的映射相对容易。这种映射将根据数据库的选择而变化,但只需一种机制即可使用选定的 NoSql 数据库机制来持久化我们的模型。另一方面,如果我们选择关系数据库,那么我们需要设计一种执行对象关系映射的机制。为此,我们有两种选择。我们可以直接创建自己的数据访问层,使用经典的 ADO.NET。这样,数据访问层将以领域模型的形式进行通信,并反过来为应用程序执行数据库交互。第二个选择是有一个使用对象关系映射器 (ORM) 的数据访问层,这将简化数据访问过程。这相对容易且耗时较少,但这可能会对性能产生一些影响,因为 ORM 通常比原始 ADO.NET 调用稍慢。
注意:选择使用关系数据库还是 NoSql 数据库主要取决于我们将来是否需要扩展数据库(scale up)或横向扩展数据库(scale out)。关系数据库相对容易向上扩展,而 NoSql 数据库更容易横向扩展。
设计我们的数据访问层
假设我们选择 SQL Server 作为我们的数据库(像大多数企业应用程序一样),并选择 Entity Framework 作为 ORM。我们现在需要制定一项策略来有效使用 Entity Framework。Entity Framework 提供了三种不同的数据处理方式。
- 数据库优先:当数据库架构将要创建或已经预先存在且成熟时,应使用此方法。
- 代码优先:当我们在增量开发新应用程序时,应使用此方法,我们将主要处理我们的模型类(POCO),并且这些模型不需要担心持久化机制。稍后可以使用 Entity Framework 将它们保存在数据库中。
- 模型优先:当我们想在开始应用程序开发之前设计数据库架构时,应使用此方法。但我们的架构是持久化无关的,也就是说,我们可以定义一个架构(模型),然后使用这个模型。稍后使用 Entity Framework,我们可以将此模型持久化到 SQL Server 等数据库中。
注意:请参考以下文章以了解更多关于数据访问层设计指南和 Entity Framework 的信息
- 使用 N 层架构创建 ASP.NET 应用程序[^]
- 理解 ADO.NET 的初学者教程[^]
- 面向绝对初学者的 Entity Framework 入门介绍[^]
- 面向绝对初学者理解 Entity Framework 的 Code First 方法在 ASP.NET MVC 中的教程[^]
使用数据访问层
一旦我们准备好了数据层(例如 SQL Server)和数据访问层(使用 Entity Framework),就该着手处理将在这些层之上工作的层,并使用这些层了。通常,这就是我们希望 ASP.NET MVC 应用程序介入的地方。我们现在需要做的是在数据访问层之上构建一个 ASP.NET MVC 应用程序。有了这个,我们首先需要处理如何从我们的 MVC 应用程序访问数据访问层。一种简单的访问数据访问层的方法是将 DBContext 类直接放在我们的控制器中并使用它们。这种方法适用于每个控制器仅作用于一个模型的情况。但是,如果我们想使用多个模型并从控制器对多个数据库实体执行数据库操作,这种方法将不起作用。为什么?答案是 `DBContext` 依赖于某些内部机制来实现工作单元并执行数据库操作。ASP.NET MVC 是无状态的,这使得 Entity Framework 无法与此内部机制正常工作。
为了规避这个问题,我们需要在控制器和数据访问层之间引入一个抽象层,以便所有进出我们数据访问层的数据和控制流都由这个抽象的中间层控制。这个抽象层可以使用存储库和工作单元模式轻松实现。这些模式在业务逻辑层和数据访问层之间提供了更好的抽象,使这两者可以独立更改。
注意:要了解更多信息,请阅读
使用此模式的另一个好处是它使我们的 ASP.NET MVC 应用程序和业务逻辑具有可单元测试性。由于我们的 MVC 应用程序使用存储库类进行数据访问,因此我们的测试项目可以轻松传入模拟存储库来模拟数据。
注意:要了解更多信息,请阅读
深入 ASP.NET MVC
在我们进入 ASP.NET MVC 的精彩世界之前,让我们再次讨论关注点分离。关注点分离是一种概念,它规定软件组件的设计方式应该是每个组件只解决一个关注点。每个这些关注点彼此独立,也就是说,它们可以独立更新而不会影响其他任何组件。实际上,在大多数情况下,它们也可以独立开发。N 层架构是关注点分离的完美例子。到目前为止,我们在本文中讨论的内容也符合 SoC 概念。其中数据访问层独立于数据层,业务逻辑层独立于数据访问层。并且存储库模式的使用进一步减少了耦合,并且它们中的每一个都可以独立更改。
到目前为止我们讨论的所有内容都可以轻松地与 Web Forms 应用程序一起使用。那么为什么是 MVC? well, ASP.NET MVC 还在 UI 层提供了关注点分离。随着客户端 JavaScript 技术在应用程序中的使用越来越多,ASP.NET MVC 在视图、模型和控制器之间提供了清晰的分离。这使得应用程序的设计方式变得容易,即应用程序 UI 的一部分可以配置为显示在服务器上处理的数据,而应用程序 UI 的一部分可以仅包含客户端代码。
注意:这是 ASP.NET MVC 的众多好处之一。ASP.NET MVC 最棒的部分在于它在视图、模型和控制器之间提供了松耦合。这使用户应用程序开发人员能够开发更易于维护、可测试和更灵活的应用程序。考虑到这一点,让我们看看应该将什么放在哪里以充分利用 ASP.NET MVC 应用程序架构。
模型
模型通常是表示领域实体的 C# 类。在任何应用程序中,我们可能需要不同类型的模型。
- 数据模型:数据模型是关系实体的对象表示。此模型将包含与数据库实体对应的属性以及此数据模型的验证规则。当我们与数据访问层通信时,通常会使用此模型。
- 领域模型:领域模型将包含应用程序的业务逻辑。此模型处理所有业务规则验证,然后转换为数据模型,以便可以将其传递给数据访问层。应用程序的顶层使用领域模型,当我们想与数据访问层通信时,此领域模型将被映射到数据模型,然后传递给数据访问层。此映射可以手动完成,也可以使用 AutoMapper 等工具完成。[如果模型仅包含数据规则验证和 CRUD 操作,则数据模型可以用作领域模型。使用 Entity Framework,我们可以将实体或 POCO 类同时用作数据模型和领域模型。]
- 视图模型:视图模型通常包含两个或多个领域模型。创建它主要是因为表示层期望来自多个领域模型的数据。在 ASP.NET MVC 中,如果我们想显示来自多个领域模型的数据,那么可以创建一个视图模型,该模型将包含这两个模型。然后,控制器可以创建此视图模型并将其与视图关联。视图模型是多个领域模型的聚合。
控制器
控制器是促进模型和视图之间交互的类。通常,控制器会在任何用户操作时被路由器调用,然后会调用控制器的操作方法。然后,它会创建一个模型对象,然后将其传递给某个视图。所以,我们可以说 Action method 包含实际的逻辑。对于每个用户操作,都会有一个 action method,这些 action method 将被包装在一个控制器类中,以便可以通过路由引擎调用它们。
现在有人可能会说,我们可以有一个包含所有 action method 的大控制器。这可行,但我们会牺牲单一职责原则。通常,创建控制器的一种方法是每个模型(领域模型或视图模型)一个控制器。这样,只有与一个模型相关的操作才会包含在各个控制器中。另一种方法是从用户视图的角度考虑,为每个用户视图创建一个控制器。(用户视图在此处指的是一个用例,例如“管理账户”)。任何一种方法都可以,但我们需要记住的是,我们创建控制器的方式是使它们能够提供合理的关注点分离并只承担单一职责。
视图
视图是用户将看到并用于在应用程序上执行操作的屏幕。对于普通视图,数据可以从控制器传递到视图的 `ViewDataDictionary`。或者,我们可以使用强类型视图将视图绑定到特定的模型或视图模型。当我们需要跨多个页面重用 UI 的一部分时,局部视图非常有用。主视图或布局可用于在多个视图之间创建一致的布局。
我们还可以创建本身就是单页应用程序的视图,也就是说,视图将包含客户端 JavaScript 代码来处理用户操作。在这种情况下,视图可以异步调用控制器的一些操作,该操作将返回 JSON 或 XML。对于此类视图的另一种选择是向 Web 服务或 Web API 发送 AJAX 请求,并检索该视图的结果。拥有一个完整的客户端视图(即单页应用程序)或拥有一个 ASP.NET 视图的决定,将主要取决于视图中所需的交互级别。如果我们想开发高度交互的桌面式用户体验,那么客户端 JavaScript 是最佳选择,否则一个简单的 ASP.NET MVC 视图应该足够了。
关注点
关注点分离也许是 ASP.NET MVC 的主要优势。但是 ASP.NET MVC 框架足够灵活,可以违反规则并偏离关注点分离原则。在设计 ASP.NET 应用程序时,我们应该从一开始就牢记 SoC。我们讨论的起点是谈论通过设计适当的数据层、数据访问层、业务逻辑层和表示层来实现应用程序架构级别的 SoC。然后我们看到了如何通过实现存储库模式来实现松耦合的业务逻辑层和数据访问层。最后,我们研究了一些关于如何在 ASP.NET MVC 应用程序中创建模型、控制器和视图的要点。
本文主要偏向理论,展示了一种处理应用程序设计的方法。还有其他可能更好的方法。这是我在与主要从事 Web Forms 的 MVC 新手讨论时想出的。我希望这能有所帮助。
历史
- 2013 年 12 月 03 日:初版。