Entity Framework 学习

 

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 的所有基于文本的语法。

 

解释 EntityClient 和对象服务之间的差异,并介绍将 LINQ 和 EntitySQL 与这些服务一起使用的意义。可使用 LINQ 获取实体时为什么要使用实体 SQL?

介绍可用于与 EDM 进行交互的三种主要技术:

· 使用 EntityClient 提供程序编写 EntitySQL 查询

· 使用对象服务编写 EntitySQL 查询

· 使用对象服务编写 LINQ 查询

以上每种技术都拥有共同的特征;例如,都直接或间接使用 EntityClient 提供程序。但是,它们产生的结果以及获得这些结果的方式却有所不同。

EntityClient 提供程序具有一系列对象,如果了解 ADO.NET 对象模型,则您应该熟悉这些对象。EntityConnection 用于连接到 EDM,EntityCommand 用于针对 EDM 发出查询,而命令的结果则通过 DbDataReader 返回。无论是通过对象服务直接还是间接使用 EntityClient,EntityClient 最终都会发出查询并返回结果。

这使得问题再次摆上桌面,为什么有 LINQ 却要使用 EntitySQL?答案在于每种技术的优缺点。

 

EntityClient + EntitySQL

深入分析:实体框架数据加载

许多对象关系映射框架都支持以下两种众所周知的行为,它们有助于编写短期“探测”代码、改进应用程序使用数据库的次数和数据传输量:

· 预先加载:每次程序需要操作一组相关对象时,都会在一个先期请求中从数据库冻结整个图。

· 延迟加载:当程序导航连接对象图的属性时自动从数据库加载数据,此时可能多次访问存储库,但仅加载必需的项目。

为遵循“无隐藏网络往返操作”原则,实体框架放弃了自动化延迟加载。遍历由代码生成的类所组成的图这一操作永远不会针对存储库触发查询。实体框架代之以要求用户代码在使用之前明确冻结所有对象。

可随时加载,但在每次要执行加载时需针对引用或集合调用称为 Load 的方法。实际上,通过干涉代码生成过程或通过编写自己的类,仍可实现隐式延迟加载。但是,更常见的方法是延迟加载而非仅隐藏往返操作。您必须了解,每次程序使用延迟加载时,都容易得到一个包含不一致数据的图。并且,如果程序不及时刷新已加载的数据,图中的数据就可能会失效。

通过使用 EntityClient API 编写代码,可最精细地控制这三种技术。可创建 EntityConnection 来连接 EDM,以 EntitySQL 编写一个查询并使用 EntityCommand 执行该查询,然后通过 DbDataReader 返回结果。此技术要精简一些,省去了 LINQ 和对象服务提供的一些语法修饰。

EntitySQL 最大的优点是其灵活性。基于字符串的语法有助于轻松地构建动态查询。如果需要创建临时查询,它将非常有用。但是,这种灵活和精简也使得只能通过 DbDataReader 返回结果。无法使用 EntityClient 和 EntitySQL 从 EDM 返回纯实体。DbDataReader 可供检索并用于在满足 EntitySQL 查询的行集合中执行迭代。

 

对象服务 + EntitySQL

另一技术是使用对象服务并利用 EntitySQL 来执行查询。从而不再与 EntityClient 提供程序直接交互(尽管实际上它仍会与提供程序通信)。使用 ObjectContext 和 ObjectQuery<T> 来针对 EDM 发出查询。

此技术非常适合于发出临时查询(与第一种技术相同)。但是,它不是通过 DbDataReader 返回数据,将对象服务与 EntitySQL 一起使用时可从 EDM 返回实体。因此,可牢靠地提供以下优点:查询灵活且返回最佳实体。可使用此技术从 EDM 检索实体,然后使用 ObjectContext 中的 SaveChanges 方法来更新实体。

 

对象服务 + LINQ

将对象服务 LINQ 一起使用并不适合于临时查询,这一点与其他技术不同。

因此,为什么有 LINQ 却要使用实体 SQL?如果需要临时查询或希望创建比使用 LINQ 实现的查询更加灵活的查询,则应使用实体 SQL。否则,建议使用 LINQ 和对象服务,以便可受益于其强类型化以及返回实体和投影的功能。

如使用 LINQ 的强类型化语法,还可在设计时发现许多错误,不必等到运行应用程序时才发现。我非常喜欢这一功能——它使我可以一心一意编写代码,不必通过执行构建和运行来找出错误。

ObjectContext 的作用是什么? ObjectContext 是到对象服务的 EntityConnection 的通道。它通过底层 EntityConnection 提供对 EDM 的访问。例如,可通过 ObjectContext 访问实体,询问 ObjectContext 以找出有关对象状态的信息,以及使用 CreateQuery 方法创建 ObjectQuery<T> 查询。

ObjectContext 的另一目的是为对象提供获取数据库条目更新信息的方法。例如,可使用 ObjectContext 方法来将实体添加到 ObjectContext、删除实体、操作实体以及最终将实体更改保存到数据库(通过 SaveChanges 方法)。

 

结束语

clip_image002

System.Data.Objects (System.Data.Entity.dll)

该命名空间包含一些类,用于提供对对象服务的核心功能的访问。 这些类使您可以藉由作为实体类型实例的强类型 CLR 对象来查询、插入、更新和删除数据。 对象服务支持针对实体数据模型 (EDM) 中定义的类型进行的语言集成查询 (LINQ)  ESQL 查询。对象服务将返回的数据具体化为对象,并将对象更改传播回数据源。 它还提供了用于跟踪更改、将对象绑定到控件以及处理并发问题的功能。

 

 EntityClient 提供程序的基础上,实体框架添加了另一组抽象,以便允许针对对象而非 EntityClient 返回的非类型化数据记录进行开发。这就是通常被认为是 ORM 的层,它可以生成在数据模型中所定义类型的 CLR 实例并允许开发人员使用 LINQ  ESQL 查询这些对象。它也恰好是当初众多开发人员在市场中寻找可用的 ORM 技术时最能吸引他们眼球的实体框架层。

 

对象服务层的高级功能是接受来自应用程序的 ESQL  LINQ 查询,然后将查询表达式传递给下面的 EntityClient 并返回IEnumerable<T>。但是,经过深入的分析后您会发现,对象服务层的中心是代表应用程序与底层数据存储之间的交互会话的ObjectContext

 

ObjectContext 是开发人员在查询、添加和删除其实体实例以及将新状态保存回数据库时用到的主要构造。

 

如果使用对象服务,则对开发人员而言,跟踪内存中对象所发生的更改的流程以及将这些更改保存回数据库的流程都会得到简化。对象服务使用 ObjectStateManager 不但会跟踪内存中实例的当前状态,还会跟踪每个实例从存储库中检索出来时的初始状态,从而使实体框架可以在将数据推送回数据库时应用最优的并发操作。通过对 ObjectContext 调用 SaveChanges 方法,可以轻松地保存所跟踪的更改并将其推送回数据存储库。

 

注:http://msdn.microsoft.com/zh-cn/library/system.data.objects.aspx

 

ObjectContext

ObjectContext 封装 .NET Framework 和数据库之间的连接。此类用作创建读取更新删除操作的网关。 它是以对象(这些对象是概念模型中定义的实体类型的实例)的形式与数据进行交互的主要类。

 

ObjectContext 类的实例封装以下内容:

· 数据库的连接,以 EntityConnection 对象的形式封装。

· 描述该模型的元数据,以 MetadataWorkspace 对象的形式封装。

· 用于管理缓存中持久保存的对象的 ObjectStateManager 对象。

 

成员参考:http://msdn.microsoft.com/zh-cn/library/system.data.objects.objectcontext_members.aspx

 

构造函数

ObjectContext(EntityConnection) :使用给定连接初始化 ObjectContext 类的新实例。 在构造过程中,从 EntityConnection 对象提取元数据工作区。

ObjectContext(String) :使用给定的连接字符串和默认的实体容器名称初始化 ObjectContext 类的新实例。

 

常用方法

AcceptAllChanges:接受在对象上下文中对对象所做的所有更改。

AddObject :将对象添加到对象上下文。

ApplyCurrentValues<TEntity> :将标量值从提供的对象复制到 ObjectContext 中具有相同键的对象中。

ApplyOriginalValues<TEntity> :将标量值从提供的对象复制到 ObjectContext 中具有相同键的对象的原始值集中。

Attach :在对象具有实体键时将对象或对象图附加到对象上下文。

AttachTo :将对象或对象图附加到特定实体集中的对象上下文。

CreateDatabase :使用当前数据源连接和 StoreItemCollection 中的元数据创建数据库。

CreateDatabaseScript :生成数据定义语言 (DDL) 脚本,该脚本为 StoreItemCollection 中的元数据创建架构对象(表、主键、外键)。

CreateEntityKey :为特定对象创建实体键,如果实体键已存在,则返回该键。

CreateObject<T> :创建并返回所请求的类型的实例。

CreateObjectSet<TEntity>() :创建新的 ObjectSet<TEntity> 实例,该实例用于查询、添加、修改和删除指定实体类型的对象。

CreateObjectSet<TEntity>(String) :创建新的 ObjectSet<TEntity> 实例,该实例用于查询、添加、修改和删除具有指定类型和指定实体集名称的对象。

CreateProxyTypes :为提供的枚举中的每个类型生成可用于 Entity Framework 的等效类型。

CreateQuery<T> :使用指定查询字符串在当前对象上下文中创建 ObjectQuery<T>

DatabaseExists :检查在当前数据源连接中指定为数据库的数据库是否在数据源上存在。

DeleteDatabase :删除在当前数据源连接中指定为数据库的数据库。

DeleteObject :将对象标记为待删除。

Detach :从对象上下文移除对象。

DetectChanges :确保 ObjectStateEntry 更改与由 ObjectStateManager 跟踪的所有对象中的更改进行同步。

Equals(Object) :确定指定的 Object 是否等于当前的 Object (继承自 Object。)

ExecuteFunction(String, ObjectParameter[]) :执行在数据源中定义并在概念模型中表示的存储过程或函数,丢弃该函数返回的任何结果,并返回执行该函数影响的行数。

ExecuteFunction<TElement>(String, ObjectParameter[]) :使用指定的参数,执行在数据源中定义并在概念模型中映射的存储过程或函数。 返回类型化的 ObjectResult<T>

ExecuteFunction<TElement>(String, MergeOption, ObjectParameter[]) :使用指定的参数和合并选项,执行在数据源中定义并在概念模型中表示的给定存储过程或函数。 返回类型化的 ObjectResult<T>

ExecuteStoreCommand :利用现有连接对数据源直接执行任意命令。

ExecuteStoreQuery<TElement>(String, Object[]) :对数据源直接执行查询,此查询将返回类型化结果的序列。

ExecuteStoreQuery<TEntity>(String, String, MergeOption, Object[]) :对数据源直接执行查询,此查询将返回类型化结果的序列。 指定实体集和合并选项,以便能够将查询结果作为实体进行跟踪。

GetHashCode :用作特定类型的哈希函数。 (继承自 Object。)

GetObjectByKey :返回具有指定实体键的对象。

公共方法 静态成员 GetObjectType :返回与指定类型的代理对象关联的 POCO 实体的实体类型。

LoadProperty(Object, String) :通过指定的导航属性并使用默认合并选项,显式加载与提供的对象相关的对象。

LoadProperty(Object, String, MergeOption) :通过指定的导航属性并使用指定的合并选项,显式加载与提供的对象相关的对象。

LoadProperty<TEntity>(TEntity, Expression<Func<TEntity, Object>>) :通过指定的 LINQ 查询并使用默认合并选项,显式加载与提供的对象相关的对象。

LoadProperty<TEntity>(TEntity, Expression<Func<TEntity, Object>>, MergeOption) :通过指定的 LINQ 查询并使用指定的合并选项,显式加载与提供的对象相关的对象。

Refresh(RefreshMode, IEnumerable) :使用数据源中的数据更新对象上下文中的对象集合。

Refresh(RefreshMode, Object) :使用数据源中的数据更新对象上下文中的对象。

SaveChanges() :将所有更新保存到数据源并重置对象上下文中的更改跟踪。

SaveChanges(SaveOptions) :使用指定的 SaveOptions 将所有更新保存到数据源。

Translate<TElement>(DbDataReader) :将包含实体数据行的 DbDataReader 转换为请求的实体类型的对象。

Translate<TEntity>(DbDataReader, String, MergeOption) :在特定的实体集中,使用指定的合并选项将包含实体数据行的DbDataReader 转换为请求的实体类型的对象。

TryGetObjectByKey :返回具有指定实体键的对象。

 

属性

CommandTimeout :获取或设置所有对象上下文操作的超时值(以秒为单位)。 null 值表示将使用基础提供程序的默认值。

Connection :获取对象上下文使用的连接。

ContextOptions :获取 ObjectContextOptions 实例,该实例包含影响 ObjectContext 的行为的选项。

DefaultContainerName :获取或设置默认容器名称。

MetadataWorkspace :获取对象上下文使用的元数据工作区。

ObjectStateManager :获取对象上下文用于跟踪对象更改的对象状态管理器。

 

事件

ObjectMaterialized :当使用数据源中的数据创建新的实体对象(作为查询或加载操作的一部分)时发生。

SavingChanges :在将更改保存到数据源时发生。

 

增加:AddObject

代码片断:

using (var edm = new NorthwindEntities())

{

Customers c = new Customers { CustomerID = "notin",CompanyName="Astar"};

edm.AddObject("Customers", c);

int result = edm.SaveChanges();

 

Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "notin");

Console.WriteLine("CustomerId={0},CompanyName={1}", addc.CustomerID, addc.CompanyName);

}

 

主从添加:

using (var edm = new NorthwindEntities())

{

Categories categorie1 = new Categories { CategoryName = "ASP.NET" };

Categories categorie2 = new Categories { CategoryName = "SQLServer" };

Products product1 = new Products { ProductName = "ASP.NET 入门到精通", Discontinued = true };

Products product2 = new Products { ProductName = "SQLServer 入门到精通", Discontinued = false };

 

categorie1.Products.Add(product1);

categorie2.Products.Add(product2);

 

edm.Categories.AddObject(categorie1);

edm.Categories.AddObject(categorie2);

 

edm.SaveChanges();

 

Console.WriteLine("LinqToEntities...");

var categories = from categorie in edm.Categories

where categorie.CategoryName == "ASP.NET" || categorie.CategoryName == "SQLServer"

select categorie;

 

foreach (var categorie in categories)

{

Console.WriteLine("分类: {0}", categorie.CategoryName);

foreach (var product in categorie.Products)

{

Console.WriteLine("产品: {0}", product.ProductName);

}

}

 

Console.WriteLine("EntitySQL...");

//var esql = "select value c from Categories as c where exists(c.Products)"; //查询全部

var esql = @"select value c from Categories as c where c.CategoryName = 'ASP.NET' or c.CategoryName = 'SQLServer'";

var categoriesESQL = edm.CreateQuery<Categories>(esql);

foreach (var categorie in categoriesESQL)

{

Console.WriteLine("分类: {0}", categorie.CategoryName);

foreach (var product in categorie.Products)

{

Console.WriteLine("产品: {0}", product.ProductName);

}

}

}

 

删除:DeleteObject

代码片断:

using (var edm = new NorthwindEntities())

{

Customers c = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "notin");

edm.DeleteObject(c);

int result = edm.SaveChanges();

 

Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "notin");

//输出异常,因为已为NULL。

Console.WriteLine("CustomerId={0},CompanyName={1}", addc.CustomerID, addc.CompanyName);

}

 

修改:SaveChanges

代码片断:

using (var edm = new NorthwindEntities())

{

Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "notin");

//addc.CustomerID = "notupdate"; //主键不可修改

addc.CompanyName = "Xlovey";

int result = edm.SaveChanges();

 

Customers updatec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "notin");

Console.WriteLine("CustomerId={0},CompanyName={1}", updatec.CustomerID, updatec.CompanyName);

}

 

结果:

 

执行SQL语句: ExecuteStoreCommand

代码片断:

using (var edm = new NorthwindEntities())

{

string sqlStr = @"insert into Customers(CustomerID, CompanyName) values (@CustomerID, @CompanyName)";

var parm = new DbParameter[] {

new SqlParameter { ParameterName = "CustomerID", Value = "CR"},

new SqlParameter { ParameterName = "CompanyName", Value="Cnblogs"}

};

 

int rowCount = edm.ExecuteStoreCommand(sqlStr, parm);

 

Console.WriteLine("{0} rows inserted", rowCount.ToString());

 

Customers insertC = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "CR");

Console.WriteLine("CustomerId={0},CompanyName={1}", insertC.CustomerID, insertC.CompanyName);

}

 

结果:

 

执行SQL语句并返回对象:ExecuteStoreQuery<TElement>

代码片断:

string sqlStr = "select * from Customers where CompanyName = @CompanyName";

var parm = new DbParameter[] {

new SqlParameter {ParameterName = "CompanyName", Value = "Astar"}};

var customers = edm.ExecuteStoreQuery<Customers>(sqlStr, parm);

 

foreach (var customer in customers)

{

Console.WriteLine("{0},{1}",customer.CustomerID, customer.CompanyName);

}

 

ObjectStateEntry

维护对象或关系的 EntityStateEntityKey 值和原始值。还管理已修改属性的列表。每个实体类型关系实例都与一个ObjectStateEntry 实例关联。 仅当对象在 ObjectStateManager 中时,才与 ObjectStateEntry 关联。当具有关系的对象分离时,由 ObjectStateEntry 维护的信息会减少为维护关系所需的信息。一个 ObjectStateEntry 不能有与同一 ObjectStateManager 中的另一个 ObjectStateEntry 相同的键。

 

无法修改持久性实体的键值。 处于更改、已修改和已删除状态的实体是持久性实体。

 

成员参考:http://msdn.microsoft.com/zh-cn/library/system.data.objects.objectstateentry_members.aspx

 

ObjectStateManager

ObjectStateManager 跟踪查询结果,并提供逻辑来合并多个重叠的查询结果。 它还在用户插入、删除或修改对象时执行内存中的更改跟踪,并提供用于更新的更改集。 更改处理器使用此变更集来持久保存修改。

 

此类通常由 ObjectContext 使用,不直接用于应用程序中。

 

成员参考:http://msdn.microsoft.com/zh-cn/library/system.data.objects.objectstatemanager_members.aspx

 

ObjectQuery

ObjectQuery 类支持对实体数据模型 (EDM) 执行 LINQ to Entities  Entity SQL 查询。ObjectQuery 还实现了一组查询生成器方法,这些方法可用于按顺序构造等效于 Entity SQL 的查询命令。每个查询生成器方法返回 ObjectQuery 的一个新实例。使用这些方法可以构造查询,而查询的结果集基于前面 ObjectQuery 实例序列的操作。

 

常用方法和属性

Execute():使用指定的合并选项执行非类型化对象查询。

ToTraceString():返回要对数据源执行的命令。用于追踪所执行的SQL语句,通过此方法我们可以获取所执行的SQL语句,以便我们查看、分析具体执行的SQL语句。

CommandText:返回查询的命令文本。

 

Execute方法:返回ObjectResult

代码片断:

using (var edm = new NorthwindEntities())

{

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

 

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

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

foreach (Customers c in query)

{

Console.WriteLine(c.CustomerID);

}

}

 

MergeOption有四种值:

· AppendOnly 只追加新实体,不修改以前获取的现有实体。这是默认行为。

· OverwriteChanges  ObjectStateEntry 中的当前值替换为存储区中的值。这将使用服务器上的数据重写在本地所做的更改。

· PreserveChanges 将替换原始值,而不修改当前值。这对于在发生开放式并发异常之后强制成功保存本地值非常有用。

· NoTracking 将不修改 ObjectStateManager,不会获取与其他对象相关联的关系,可以改善性能。

 

GetResultType方法:返回查询结果的类型信息。

 

ToTraceString方法:获取当前执行的SQL语句。

Console.WriteLine(query.ToTraceString());

 

Where方法

代码片断:

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

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

 

//使用ObjectParameter的写法

query = query.Where("it.CustomerId=@customerid");

query.Parameters.Add(new ObjectParameter("customerid""ALFKI"));

 

//也可以这样写

//ObjectQuery<Customers> query = edm.Customers.Where("it.CustomerID='ALFKI'");

 

foreach (var c in query)

{

Console.WriteLine(c.CustomerID);

}

 

First/ FirstOrDefault方法

代码片断:

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

 

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

 

//如果没有符合条件的数据,那么我们的代码将会抛出异常。

Customers c1 = query.First();

 

//如果没有符合条件的数据,那么它将返回null。

Customers c2 = query.FirstOrDefault();

 

Console.WriteLine(c1.CustomerID);

Console.WriteLine(c2.CustomerID);

 

Distinct方法:返回不重复的项。

代码片断:

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

 

ObjectQuery<string> query = edm.CreateQuery<string>(sqlStr);

query = query.Distinct();

foreach (string c in query)

{

Console.WriteLine("City {0}", c);

}

 

 

Except方法:返回两个查询的差集。

代码片断:

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

ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(sqlStr);

 

string sqlStr2 = "select value c from NorthwindEntities.Customers as c where c.Country='UK' order by c.CustomerID limit 10";

ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(sqlStr2);

 

query1 = query1.Except(query2);

foreach (Customers c in query1)

{

Console.WriteLine(c.Country);

}

 

Intersect方法:返回两个查询的交集。

代码片断:

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

ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(sqlStr);

 

string sqlStr1 = "select value c from NorthwindEntities.Customers as c where c.Country='UK' order by c.CustomerID limit 10";

ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(sqlStr1);

 

query1 = query1.Intersect(query2);

foreach (Customers c in query1)

{

Console.WriteLine(c.Country);

}

 

Union/UnionAll方法:返回两个查询的合集,包括重复项。其中UnionAll必须是相同类型或者是可以相互转换的。

 

Include方法:可通过此方法查询出与相关的实体对象,相当于子查询,可以分析生成的SQL语句。

代码片断:

string sqlStr = "select value c from NorthwindEntities.Customers as c WHERE c.CustomerID ='HANAR'";

 

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

query = query.Include("Orders");

foreach (Customers c in query)

{

Console.WriteLine("{0},{1}", c.CustomerID, c.Orders.Count);

}

 

Console.WriteLine(query.ToTraceString());

 

OrderBy方法

代码片断:

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

 

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

query.OrderBy("it.country asc,it.city asc");

 

//也可以这样写

//query.OrderBy("it.country asc");

//query.OrderBy("it.city asc");

foreach (Customers c in query)

{

Console.WriteLine("{0},{1}", c.Country, c.City);

}

 

Select方法:只查询需要的数据项。

代码片断:

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

 

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

ObjectQuery<DbDataRecord> records = query.Select("it.customerid,it.country");

 

foreach (DbDataRecord c in records)

{

Console.WriteLine("{0},{1}", c[0], c[1]);

}

 

Console.WriteLine(records.ToTraceString());

 

输出结果:

ALFKI,Germany

ANATR,Mexico

ANTON,Mexico

AROUT,UK

BERGS,Sweden

BLAUS,Germany

BLONP,France

BOLID,Spain

BONAP,France

BOTTM,Canada

SELECT TOP (10)

1 AS [C1],

[Extent1].[CustomerID] AS [CustomerID],

[Extent1].[Country] AS [Country]

FROM [dbo].[Customers] AS [Extent1]

ORDER BY [Extent1].[CustomerID] ASC

 

SelectValue方法:查询结果投影为属性。

代码片断:

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

 

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

ObjectQuery<string> records = query.SelectValue<string>("it.customerid");

foreach (string c in records)

{

Console.WriteLine("{0}", c);

}

 

Skip/Top方法:查询分页,核心利用SQLServer分页函数。

代码片断:

string sqlStr = "select value c from NorthwindEntities.Customers as c order by c.CustomerID ";

 

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

query = query.Skip("it.customerid asc""10");

query = query.Top("10");

foreach (Customers c in query)

{

Console.WriteLine("{0}", c.CustomerID);

}

 

Console.WriteLine(query.ToTraceString());

 

 

posted @ 2014-03-21 15:37  hq5460  阅读(387)  评论(0编辑  收藏  举报