Entity Framework 学习总结之四:对象服务介绍使用
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
维护对象或关系的 EntityState、EntityKey 值和原始值。还管理已修改属性的列表。每个实体类型关系实例都与一个 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());
参考:
《Entity Framework 4.0 Recipes》