Entity Framework 学习总结之三:架构组成介绍
为什么需要建立另外一种数据模型?
那么为什么需要建立另外一种模型呢?随着公司数据处理量的增加,理顺数据关系并基于这些数据来开发应用程序变得非常困难。数据库架构的设计需要考虑存储问题(如数据完整性、性能和管理),有时候这不是很容易理解。这些架构还经常与应用程序的结构有冲突,使开发和维护工作变得更加复杂。
我们经常会遇到数据结构与所构建的应用程序被分割开的自定义解决方案。遗憾地是,对每个应用程序而言,自定义解决方案的数量、各种各样的方法以及建模数据所需的步骤都各不相同,导致问题不断产生。整个行业都希望能有一种方法来针对应用程序级的域模型进行定义和开发,以便能够与逻辑模型的存储清晰地分隔开。因此引入了实体框架。
EDM允许以组织看待和使用数据的方式(不是数据的存储方式)来定义域模型。开发 EDM 还有一个主要目标,那就是在 Microsoft 内成为用于开发人员和服务器技术套件的核心数据模型。
通过使用一个核心数据模型,应用程序的维护得以简化。实现此目标后,EDM 即可发挥作用,不但可以为基于 ADO.NET Entity Framework 构建的自定义应用程序定义模型,还可以作为报告和可视化应用程序、Intranet 门户应用程序或工作流应用程序的输入内容。
与 ER 模型类似,EDM 使用以下两个主要概念:实体(事物)以及这些实体之间的关系(或关联)。在考虑实体和关联实例(或这些实例集操作的封闭包)的存储时,还需要用到“集”的概念。因此,更清楚地说,实体存在于 EntitySet 中而关联存在于 AssociationSet 中。
在 EDM 中定义的最后一个结构概念是 EntityContainer 概念,它可以在之前介绍的实例和关系集周围定义一个封闭包。这些简单概念在联合使用后可允许开发人员定义一个域模型,此模型可被映射回持久性层和在应用程序自身中使用的类。(需要注意的是,EDM 的持久性层不需要是相关的,尽管它现在是相关的。)
EDM 中定义的每个实体类型可包含两个不同类型的成员,一个是用来定义实体的属性(类似于数据库中的列),另一个是导航属性,它支持实体类型参与的导航关系(通常表示为数据库中的外键)。此外,每个实体类型都必须有一个唯一的标识或关键字。
为什么使用 XML 来描述 EDM?
经过认真考虑后,XML 被选作 EDM 的第一个序列表示。通过生成 XML 文件(或资源)或从动态生成的 XML 表示中加载,开发人员和第三方可以利用经过良好定义的 XML 格式来进行此类格式的转换以及将其加载到实体框架的元数据运行时中。但是,创建 EDM 的其他表示也是完全可能的,而且很可能随着产品的发展在未来版本中就会出现其他替代表示。
当前的 EDM 语法是使用产品随附的 XML 架构定义语言 (XSD) 定义的。但是,不应期望大多数人都会采用手动开发 XML 的方式,而应认为人们会使用 Visual Studio 中提供的工具。也就是说,团队已知晓大家对特定于域的语言 (DSL) 以及对 EDM 模型备用持久性机制(通常针对数据库而言)的关注,并且正在对这些选项做评估以便在即将发行的版本中进行扩展。
谁需要另一种新的查询语言?
有关 EDM 开发的最后一个问题是为什么要创建一种新的查询语言?为什么不使用现有的语言?在稍微深入地研究一个 EDM 后,答案就会变得清晰起来。
到目前为止,我已经介绍了为什么要创建 EDM 以及 EDM 中使用的各种构造,还介绍了这样一个事实,即该模型是实体关系模型的后代。为了使创建的模型不但能够清楚地映射到底层数据存储,而且还可以表现开发人员编程时所依据的应用程序级的域模型,EDM 需要能够对各种概念(如继承和多态性)进行建模。由于当前的关系查询语言并不支持基于继承、关系导航或多态结果的返回值进行查询,因此需要开发一种新的查询语言来满足此需求。
这就催生了实体 SQL (ESQL),它是一种新的 SQL 语言,其中加入了之前的 SQL 语言并不支持的基于概念的查询功能。ESQL 扩展现有 SQL 语言的方式与 EDM 扩展数据库中所使用的关系模型的方式十分类似。此外,ESQL 未绑定到任何特定于后台数据库的语法,因此可一次性编写查询(和/或应用程序),无论针对的是哪个后台数据库都无影响。下面我将以一个简单的 ESQL 查询为例,说明如何从全部博客中检索至少具有一篇帖子和关联人员(在我的模型中是指博客所有者)的博客:
select c, c.Person from travelEntitiesGeneral.Blogs as c where c.BlogPosts.Count > 0
实现 EDM
ADO.NET Entity Framework 是由 ADO.NET 演变而来的,是 EDM 的首个具体实现,可在开发关系数据库时提供较高级别的抽象。在版本 1.0 中,团队一直侧重于构建平台基础,而不仅仅是一个简单的 ORM,这将允许开发人员使用非常灵活的映射来处理概念模型或对象模型,并能够适应与底层存储之间存在的巨大分别。
这一高度的灵活性以及与底层存储的巨大分别是允许数据库和应用程序分别发展的关键所在。当数据库架构发生改变时,应用程序由于采用实体框架而不必进行改动,并且您通常不需要重新编写应用程序的内容,只是在必要时更新映射文件以适应此变化即可。
为了开始发展 ADO.NET 平台,需要以现有 ADO.NET 2.0 提供程序模型为基础构建实体框架,并对现有提供程序进行适当更新以支持新的实体框架和 ADO.NET 3.5 功能。我们之所以选择在现有 ADO.NET 提供程序模型的基础上来实现是为了确保开发社区对提供程序模型不会感到陌生。
体系结构如图 1 所示。您会注意到可接受的架构包括概念架构定义语言 (CSDL)、映射架构语言以及存储架构定义语言 (SSDL)。您还会注意到,实体框架包括了更新后的支持规范命令树 (CCT) 的 SqlClient 数据提供程序。
图 1 ADO.NET Entity Framework 体系结构
EntityClient
然后,实体框架在这些 ADO.NET 3.5 提供程序的基础上引入新的 ADO.NET 提供程序 EntityClient。EntityClient 看上去与之前使用的 ADO.NET 提供程序非常类似,它将提供第一个抽象,可允许开发人员使用标准的 Connection、Command 和 DataReader 对象依照 EDM 执行查询。它还会将映射域模型所需的客户端视图引擎(根据 EDM 定义的)添加到底层关系数据库架构。必要时,EntityClient 可借助 ESQL 查询字符串让开发人员以行和列的形式处理实体,而不必生成类来表示概念架构。
在这第一个阶段中,命令树仍针对 EDM 来表示。客户端视图引擎借鉴数据库系统中的具体化视图理论并将这些理论应用到数据访问层,它在树中应用了一个映射转换,可生成一个在底层逻辑存储模型方面表示相同操作的树,并删除任何非关系概念(如关系、继承和多态性)。这一新转换的树被传给 ADO.NET 3.5 提供程序服务,而此服务会返回封装底层存储的本机 SQL 的 DbCommand 命令,然后此命令被执行,其结果通过堆栈向上回传。
在定义客户端视图引擎中所使用的映射以便在 EDM 和逻辑数据库架构之间进行转换时,可采用多种不同的方法。此映射可使用映射规范语言 (MSL) 来指定,这是一种声明性的 XML 语法,可通过手动编写 XML 或使用 Visual Studio 中包括的实体映射工具进行创建和编辑。
编译时,MSL 允许实体框架生成必要的查询并更新视图,这些视图随后会在客户端视图引擎中被用来完成从查询(利用 EDM 定义的)到逻辑存储架构的转换。
另一种用于表达映射或部分映射的方法是使用 ESQL 查询。在这种情况下,当开发人员使用 ESQL 来表达查询视图时,基础结构会要求他们在映射规范中同时定义伴随的 Create、Update 和 Delete 映射。此操作是必需的,因为如果能够在“查询视图”中利用 ESQL 功能,从而可以为没有单个有效更新视图的查询定义视图,则映射基础结构将无法为查询生成对应的更新视图。
对象服务
在 EntityClient 提供程序的基础上,实体框架添加了另一组抽象,以便允许针对对象而非 EntityClient 返回的非类型化数据记录进行开发。这就是通常被认为是 ORM 的层,它可以生成在数据模型中所定义类型的 CLR 实例并允许开发人员使用 LINQ 或 ESQL 查询这些对象。它也恰好是当初众多开发人员在市场中寻找可用的 ORM 技术时最能吸引他们眼球的实体框架层。
对象服务层的高级功能是接受来自应用程序的 ESQL 或 LINQ 查询,然后将查询表达式传递给下面的 EntityClient 并返回 IEnumerable<T>。但是,经过深入的分析后您会发现,对象服务层的中心是代表应用程序与底层数据存储之间的交互会话的 ObjectContext。
ObjectContext 是开发人员在查询、添加和删除其实体实例以及将新状态保存回数据库时用到的主要构造。
如果使用对象服务,则对开发人员而言,跟踪内存中对象所发生的更改的流程以及将这些更改保存回数据库的流程都会得到简化。对象服务使用 ObjectStateManager 不但会跟踪内存中实例的当前状态,还会跟踪每个实例从存储库中检索出来时的初始状态,从而使实体框架可以在将数据推送回数据库时应用最优的并发操作。通过对 ObjectContext 调用 SaveChanges 方法,可以轻松地保存所跟踪的更改并将其推送回数据存储库。
到目前为止,我一直在讲述有关 ObjectContext 的基本内容并通过一些示例介绍 ObjectContext 的基本用法,它通常用于有动态工具或应用程序需要使用 EDM 模型的情形。但是,在使用 Visual Studio 作为开发环境时,开发人员发现强类型化 ObjectContext 还有一个优点,即可以向可能特定于目标 EDM 的表面功能添加属性和方法。
LINQ to Entities 是作为对象服务上一个非常薄的层出现的,它在编程语言中提供直接的查询功能而非使用基于字符串的查询。在这种情况下,ObjectQuery 类将实现 Iqueryable,允许它接受 LINQ 表达式树,并采用对象服务在将 ESQL 查询传递到 EntityClient 提供程序时所使用的同样方式在实例框架中推动查询(作为 CCT 查询表达式)。
默认情况下,无论是从 Visual Studio 中的 EDM 生成的还是使用 edmgen.exe(实体框架随附的命令行工具)生成的 CLR 类都是 XML 可序列化和二进制可序列化的,并且是导航属性默认设置为 DataMembers 的数据约定,因而可以创建 ASMX Web 服务并在视图状态或 WCF 服务中使用实体实例。
与大多数 ORM 类似,实体框架目前并不支持使用数据操作语言 (DML) 进行创建、更新或删除等操作。必须将更改应用到内存中的对象,并且在构建要保持的整个图表时可能需要与数据库进行多次交互。