Entity Framework 学习总结之一:ADO.NET 实体框架概述

ADO.NET 实体框架概述

新版本中的 ADO.NET 以新实体框架为特色。它使开发人员可以通过对象模型(而不是逻辑/关系数据模型)专注于数据。实体框架有助于将逻辑数据架构抽象为概念模型,并且允许以多种方式通过对象服务和名为“EntityClient”的新数据提供程序与概念模型交互。

实体框架使用概念层(ConceptualModels)、映射层(Mappings)和逻辑层(StorageModels)将逻辑数据库结构抽象化。EntityClient 的备选项是对象服务。具体地说,实体框架中的对象服务有助于减少开发人员需要编写的数据访问代码的数量。

 

实体框架组件

实体框架使开发人员可以编写更少的数据访问代码,减少维护,将数据结构抽象化为更易于开展业务(标准化程度较低)的方式,并且有利于数据的持久性。当与 LINQ to Entities结合使用时,由于实体框架从概念模型中生成强类型化类,还有助于降低编译时错误的数量。

实体框架生成开发人员能够依据其编写代码的概念模型。使用名为“EntityClient”的新数据提供程序和名为“Entity SQL”的新语言(类似于 T-SQL)可以直接与该模型交互。EntityClient 具有与熟悉的 ADO.NET 对象类似的模型,使用 EntityConnection 和 EntityCommand 对象返回 DbDataReader。开发人员的另一种方法是通过具有Entity SQL 的 ObjectQuery 对象或 LINQ to Entities 来使用对象服务。对象服务使开发人员可以利用概念模型的生成类,这些生成类提供了强类型化对象和持久性等特性(请参见图 1)。

clip_image002[6]

图 1 实体框架概述

这些数据访问技术使开发人员可以与 EDM 的概念实体交互。EDM 的各个层以 XML 文件形式存在;目前可以通过手动方式(或使用 Visual Studio 中的向导)使用命令行工具 (EDMGEN.EXE) 生成 EDM。

 

实体数据模型

实体框架的核心位于其模型中。实体框架支持表示数据库中的关系架构的逻辑存储模型。关系数据库通常存储数据的方式与应用程序使用数据的方式不同。通常,这会迫使开发人员按照数据库包含数据的结构检索数据。因此,开发人员通常将数据加载到更适合处理业务规则的业务实体中。在本示例中,以逻辑模型表示关系数据库的构架,业务实体表示概念模型。实体框架使用映射层在模型之间搭建了桥梁。因此,实体框架的模型中有三个处于活动状态的层:

· 概念层

· 映射层

· 逻辑层

这三层允许将数据从关系数据库映射到更加面向对象的业务模型。实体框架提供了使用 XML 文件定义这些层的方法。它还基于概念模型的架构生成了一系列类。可以针对这些类进行编程以直接与数据交互。这提供了抽象级别,因此开发人员可以针对概念模型而不是关系模型进行编程。实体框架可将针对概念模型编码的所有命令映射到逻辑模型中(请参见图 2)。

clip_image004[4]

图 2 设计实体数据模型 (单击该图像获得较大视图)

概念模型是使用概念架构定义语言 (CSDL) 在 XML 文件中定义的。CSDL 定义应用程序的业务层所知道的实体和关系。逻辑模型(表示数据库架构)是使用存储架构定义语言 (SSDL) 在 XML 文件中定义的。例如,您可能在概念模型中有一个实体,该实体实际上从数据库的多个表中派生其数据。概念模型和逻辑模型可按一对一的关系来关联实体。然而,EDM 的功能是它不必以一对一的方式链接实体。映射层(是使用映射架构语言 (MSL) 定义的)实现其他两层彼此之间的映射。此映射使开发人员可以针对概念模型编写代码,并将这些指令映射到逻辑模型。

 

生成实体数据模型

可以将数据库作为起点生成 EDM。然后,可以手动修改 XML(或者可能使用 Visual Studio 将来版本中可能提供的模型工具)。将 ADO.NET EDM 添加到项目后,向导会指导您完成创建 EDM 的过程。

第一步、创建解决方案“EntityFrameworkTest”和控制台应用程序“EFProjectCA”。

clip_image006

第二步、添加“ADO.NET实体数据模型”,命名为"NorthWindEF.edmx"。

clip_image008

接着选择“从数据库生成”,如果后期采用POCO开发模式,可以选择”空模型“。

clip_image010

clip_image012

clip_image014

clip_image016

clip_image018

到这里就创建成功了。NorthWindEF.edmx包含了CSDL、SSDL、MSL,我们可以通过记事本查看它的结构,也可以复制一份,把扩展名改为xml,在VS2010中打开。

clip_image020


分析 CSDL

驻留在 CSDL 文件中的元数据包含一系列实体和关系,其中实体由 EntityType 元素表示,关系由 Association 元素表示。实体包含一系列用于定义实体的标量属性。Key 属性指示对实体很关键的属性。复合关键字通过用空格分隔每个属性名来表示。实体还可以包含名为“NavigationProperty”的一种特殊类型的属性。这定义了如何按照关联从一个实体导航到另一个实体。

<EntityType Name="Customers">

<Key>

<PropertyRef Name="CustomerID" />

</Key>

<Property Name="CustomerID" Type="String" Nullable="false" MaxLength="5" Unicode="true" FixedLength="true" />

<Property Name="CompanyName" Type="String" Nullable="false" MaxLength="40" Unicode="true" FixedLength="false" />

<Property Name="ContactName" Type="String" MaxLength="30" Unicode="true" FixedLength="false" />

...

<NavigationProperty Name="Orders" Relationship="NorthwindModel.FK_Orders_Customers" FromRole="Customers" ToRole="Orders" />

<NavigationProperty Name="CustomerDemographics" Relationship="NorthwindModel.CustomerCustomerDemo" FromRole="Customers" ToRole="CustomerDemographics" />

</EntityType>

以下 CSDL 片段定义了 Customer 及其 Orders 之间的 AssociationSet :

<AssociationSet Name="FK_Orders_Customers" Association="NorthwindModel.FK_Orders_Customers">

<End Role="Customers" EntitySet="Customers" />

<End Role="Orders" EntitySet="Orders" />

</AssociationSet>

AssociationSet 的 End 元素表示关联的参与者。在本示例中,一个 Customers 实体与一个 Orders 实体相关联。根据多重性定义,一个 Customers 实体还可以与任意数量的 Orders 实体相关联。

EntityType 和 Association 元素定义域实体和关系的类型,而 EntitySet 和 AssociationSet 元素定义实体和关系的作用域。逻辑上应组合到一起的所有“集”都包含在 EntityContainer 元素内。

下面的 CSDL 片段显示了 EntityContainer 及其部分内容:

<EntityContainer Name="NorthwindEntities" annotation:LazyLoadingEnabled="true">

<EntitySet Name="Categories" EntityType="NorthwindModel.Categories" />

<EntitySet Name="CustomerDemographics" EntityType="NorthwindModel.CustomerDemographics" />

<EntitySet Name="Customers" EntityType="NorthwindModel.Customers" />

...

<AssociationSet Name="FK_Products_Categories" Association="NorthwindModel.FK_Products_Categories">

<End Role="Categories" EntitySet="Categories" />

<End Role="Products" EntitySet="Products" />

</AssociationSet>

<AssociationSet Name="FK_Orders_Customers" Association="NorthwindModel.FK_Orders_Customers">

<End Role="Customers" EntitySet="Customers" />

<End Role="Orders" EntitySet="Orders" />

</AssociationSet>

...

</AssociationSet>

</EntityContainer>


映射到存储

SSDL 文件定义了数据库中关系数据的结构。在本例中,它还使用了 EntityType 和 Association XML 元素来分别声明存在于数据库中的表和外键的结构。SSDL 文件的命名空间是基于 EDM 中所用数据库的名称而设置的,而其 EntityContainer 元素是以数据库架构命名的。EntityContainer 包含一系列 EntitySet 和 AssociationSet 元素,这些元素声明由 EntityType 和 AssociationType 表示的表和关系的实例。对于 SSDL 文件中的每个 EntitySet,数据库中均有一个与之对应的表。

如果从数据库中生成 EDM 并且未对 CSDL 和 SSDL 文件进行修改就立即将其打开,则您会发现这些文件极其相似。这是因为模型是直接从数据库生成的,因此概念模型直接映射到了逻辑存储。MSL 文件包含从 CSDL 到 SSDL 的直接映射。所有针对 EDM 编写的查询均将转换为生成的 SQL 命令。实体框架还支持使用存储过程而不是生成的 SQL 查询。

EntityContainerMapping 元素用于将模型 (CSDL) 映射到存储 (SSDL)。StorageEntityContainer 属性表示存储中 EntityContainer 的名称,而 CdmEntityContainer 属性表示模型中与之相对应的 EntityContainer。将模型的 EntitySet 映射到存储的 EntitySet 需要 EntitySetMapping 元素。TypeName 属性定义模型中 EntitySet 的名称,而 StoreEntitySet 属性定义存储中与之相对应的 EntitySet 的名称。通过 ScalarProperty 元素可将模型中的每个属性映射到存储。

<EntitySetMapping Name="Products">

<EntityTypeMapping TypeName="NorthwindModel.Products">

<MappingFragment StoreEntitySet="Products">

<ScalarProperty Name="ProductID" ColumnName="ProductID" />

<ScalarProperty Name="ProductName" ColumnName="ProductName" />

<ScalarProperty Name="SupplierID" ColumnName="SupplierID" />

<ScalarProperty Name="CategoryID" ColumnName="CategoryID" />

<ScalarProperty Name="QuantityPerUnit" ColumnName="QuantityPerUnit" />

<ScalarProperty Name="UnitPrice" ColumnName="UnitPrice" />

<ScalarProperty Name="UnitsInStock" ColumnName="UnitsInStock" />

<ScalarProperty Name="UnitsOnOrder" ColumnName="UnitsOnOrder" />

<ScalarProperty Name="ReorderLevel" ColumnName="ReorderLevel" />

<ScalarProperty Name="Discontinued" ColumnName="Discontinued" />

</MappingFragment>

</EntityTypeMapping>

</EntitySetMapping>


使用 EntityClient

可通过三种不同机制中的任意一种实现对实体框架的概念模型的访问(请参见图 1)。在此,我将介绍 EntityClient,即新的 .NET 数据提供程序。

当 EntityClient 使用自己的名为“Entity SQL”的基于文本的语言与概念模型通信时,会将其从逻辑存储中提取出来。所有使用 EntityClient 执行的Entity SQL 查询均被编译成发送到存储的命令树。查询从概念模型的Entity SQL 到存储的向下转换由实体框架处理。

EntityClient 中的类与常见的 ADO.NET 提供程序中的类相似。例如,使用 EntityCommand 对象执行 EntityClient 查询,这需要 EntityConnection 对象连接到 EDM。当 EntityClient 与 EDM 中的实体交互时,EntityClient 不返回实体的实例而返回 DbDataReader 对象中的所有结果。EntityClient 可以返回一组标准行和列,也可以通过 DbDataReader 返回更复杂的分层数据的表示形式。

示例使用 EntityClient 连接到概念模型并检索来自伦敦的客户列表。EntityConnection 可以接受概念层的完整连接字符串或 App.Config 文件中连接字符串的名称。连接字符串包含元数据文件(CSDL、MSL 和 SSDL 文件)列表,以及存储的专用于数据库的连接字符串信息。

using System.Data;

using System.Data.EntityClient;

using System.Configuration;

...

//string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["NorthwindEntities"].ConnectionString;

//CSharpTestEntities edm = new CSharpTestEntities();

//string connStr = edm.Connection.ConnectionString;

string connStr = "name = NorthwindEntities"; //使用三种方法获取连接字符串

using (EntityConnection conn = new EntityConnection(connStr))

{

string sqlStr = " SELECT VALUE c FROM NorthwindEntities.Customers AS c";

conn.Open();

EntityCommand ecmd = new EntityCommand(sqlStr, conn);

//ExecuteDbDataReader、ExecuteNonQuery 、ExecuteReader 、ExecuteScalar等

EntityDataReader edr = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);

while (edr.Read())

{

Console.WriteLine(edr["CompanyName"]);

}

Console.WriteLine(ecmd.ToTraceString());

}

Console.Read();


使用对象服务

与由 EDM 表示的数据进行交互的另一种方法是使用对象服务。通过对象服务,可以加载对象和导航在 EDM 中定义的任何关系。如图 1 所示,对象服务使用 EntityClient 来获取数据。对象服务增添了身份解析,使用 DataSet 时,该身份解析是手动过程。它还提供了对象持久性和事件的跟踪更改以允许显式加载和保存。这将缩短与服务器的往返路程。

对象服务允许直接返回对象列表(即可同时返回投影和定义的实体)。例如,使用对象服务,您可以按 EDM 中的定义检索 List<Customers>。可以检查 Customers 对象,更改值,然后将数据再次保存到数据库中。

如果将投影与对象服务结合使用,则返回的数据将是不可更新的对象。由于投影返回实体的特定属性而不是整个实体,对象服务无法将投影数据的更新再次保存到数据库中。如果您要更新数据,更好的选择是返回整个实体而不是使用投影。

您可以使用对象服务以使用Entity SQL 执行查询,也可以使用 LINQ to Entities 编写查询。下面的示例演示了如何使用对象服务和Entity SQL 进行查询以检索 Customers 列表:

using System.Data.Objects;

...

using (var edm = new NorthwindEntities())

{

string esql = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";

ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);

ObjectResult<Customers> results = query.Execute(MergeOption.NoTracking);

foreach (Customers c in results)

Console.WriteLine(c.CustomerID + ":" + c.CompanyName);

Console.WriteLine(query.ToTraceString());

}

在 EDM 中,EntityContainer 由从 ObjectContext(在本示例中为 northwindContext)继承的类表示。ObjectContext 类实施 ObjectQuery<T> 接口,从而使其可以使用Entity SQL 或 LINQ 创建查询。

CreateQuery 方法接受参数化的Entity SQL 语句,该语句定义了将检索 Customers 实体列表的查询。通过使用 foreach 语句对 ObjectQuery<Customers> 进行迭代时将执行作用于数据库的实际 SQL 语句。


使用 LINQ to Entities

可以在Entity SQL 中编写动态查询,并与对象服务一起使用来与 EDM 实体交互。但是,实体框架还可以与使用 LINQ to Entities 强类型化 EDM 类结合使用。例如,在刚才显示的示例中,可以将使用对象服务和Entity SQL 进行查询修改为使用 LINQ to Entities 进行查询,如下所示:

ObjectQuery<Customers> customersList = edm.Customers;

IQueryable<Customers> customersResult = from customers in customersList

where customers.City == "London"

select customers;

foreach (Customers c in customersResult)

Console.WriteLine(c.CustomerID + ":" + c.CompanyName);

此代码示例使用由 C# 3.0 支持的强类型化 LINQ 语法替代了Entity SQL 的所有基于文本的语法。


结束语

使用实体框架,开发人员可以通过对象模型(而不是逻辑/关系数据模型)专注于数据。一旦完成 EDM 的设计并将其映射到关系存储后,就可以使用 EntityClient、ObjectServices 和 LINQ 等多种技术与对象交互。

posted @ 2011-01-03 16:26  Astar  阅读(...)  评论(...编辑  收藏