首页 > 代码库 > Entity Framework学习初级篇
Entity Framework学习初级篇
1--EF基本概况
最近在学习研究微软的EF,通过这时间的学习研究,感觉这个EF目前来说还不是很完善,半成品。不过,据说在.Net4.0中,微软将推荐使用此框架,并会有所改善。而且,现在基本上所有数据库均提供了对EF的支持。因此,为以后做技术准备可以学习研究以下。但是,我个人觉得就目前来说,在实际项目慎用此框架。
下面简单的介绍以下这个EF。
在.Net Framework SP1微软包含一个实体框架(Entity Framework),此框架可以理解成微软的一个ORM产品。用于支持开发人员通过对概念性应用程序模型编程(而不是直接对关系存储架构编程)来创建数据访问应用程序。目标是降低面向数据的应用程序所需的代码量并减轻维护工作。Entity Framework 应用程序有以下优点:
·应用程序可以通过更加以应用程序为中心的概念性模型(包括具有继承性、复杂成员和关系的类型)来工作。
·应用程序不再对特定的数据引擎或存储架构具有硬编码依赖性。
·可以在不更改应用程序代码的情况下更改概念性模型与特定于存储的架构之间的映射。
·开发人员可以使用可映射到各种存储架构(可能在不同的数据库管理系统中实现)的一致的应用程序对象模型。
·多个概念性模型可以映射到同一个存储架构。
·语言集成查询支持可为查询提供针对概念性模型的编译时语法验证。
实体框架Entity Framework 是 ADO.NET 中的一组支持开发面向数据的软件应用程序的技术。在EF中的实体数据模型(EDM)由以下三种模型和具有相应文件扩展名的映射文件进行定义。
·概念架构定义语言文件(.csdl ) -- 定义概念模型。
·存储架构定义语言文件(.ssdl ) -- 定义存储模型(又称逻辑模型)。
·映射规范语言文件(.msl ) -- 定义存储模型与概念模型之间的映射。
实体框架 使用这些基于 XMl 的模型和映射文件将对概念模型中的实体和关系的创建、读取、更新和删除操作转换为数据源中的等效操作。EDM 甚至支持将概念模型中的实体映射到数据源中的存储过程。它提供以下方式用于查询 EDM 并返回对象:
·LINQ to Entities -- 提供语言集成查询(LINQ) 支持用于查询在概念模型中定义的实体类型。
·Entity SQl -- 与存储无关的 SQl 方言,直接使用概念模型中的实体并支持诸如继承和关系等 EDM 功能。
·查询生成器方法 --可以使用 LINQ 风格的查询方法构造 Entity SQl 查询。
下图演示用于访问数据的实体框架体系结构:
下面,来学习EF的基本使用方法。软件环境::
·Visual Studio 2008 +SP1
·SQl Server2005/2008
首先,建立一个名为“EFProject”的解决方案,然后添加一个名为“EFModel ”的类库项目。如下图所示。
接着,在EFModel项目中,添加“ADO.NET Entity Data Model ”项目,如下图所示:
名称取为“NorthWindEF.edmx”,然后点击“添加”。然后,在后面的步骤中,数据库选择“NorthWind”后,在选择影射对象是,把表、试图、存储过程全部都选上,其他的均保存默认的即可。最终生成的结果如下图所示。
好了,数据模型生成完毕。
最后,谈谈我认为的一些缺点:
·Edmx包含了所有对象的csdl,ssdl,msl文件,过于庞大,如果要手动修改这个文件,一不小心,眼睛看花了,就改错了。(和数据集一样的毛病)。
·目前EF支持表、试图、存储过程,其他的对象不支持,而且对使用存储过程有很多限制(目前有EFExtension提供了更多对象的支持)。
·除了MS SQl Server可直接提供这种可视化的设计界面外,其他的数据库目前还没有提供可视化设计界面(但可以自己来实现,后面介绍)。
·性能问题。(网上看到有说比ADO.Net慢700百,又有人说比ADO.net快的,具体情况我还没测试过, 但我觉得像这个些类型的框架,性能肯定是比上原生态的ADO.net慢)
好了,接下来,学习以下简单的各种操作。
2--ObjectContext、ObjectQuery、ObjectStateEntry、ObjectStateManager类的介绍
本节,简单的介绍EF中的ObjectContext、ObjectQuery、ObjectStateEntry、ObjectStateManager这个几个比较重要的类,它们都位于System.Data.Entity.dll下的System.Data.Objects命名空间下。在后续的章节中,我们经常会用到它们的某些方法,以便完成我们的某些操作或目的。本节,简单的说明一下以后我们可能会用到的各个类的方法,以方便我们后续的学习。
ObjectContext封装 .NET Framework 和数据库之间的连接。此类用作“创建”、“读取”、“更新”和“删除”操作的网关。
ObjectContext类为主类,用于与作为对象(这些对象为EDM 中定义的实体类型的实例)的数据进行交互。
ObjectContext类的实例封装以下内容:
l 到数据库的连接,以EntityConnection 对象的形式封装。
l 描述该模型的元数据,以MetadataWorkspace 对象的形式封装。
l 用于管理缓存中持久保存的对象的ObjectStateManager 对象。
ObjectContext类的成员方法以说明如下所示:
l AcceptAllChanges()
接受所有对该实体对象的更改
l AddObject(string,object)
将实体对象添加到制定的实体容器中
l ApplyPropertyChanges(string,object)
将以指派的实体对象属性的更改应用到容器中对应的原对象。
l Attach(System.Data.Objects.DataClasses.IEntityWithKey entity)
将带主键的实体对象附加到默认的容器中
l Attach(string,object)
将实体对象附加到指定的实体容器中
l CreateEntityKey(string,object)
给指定的实体对象创建实体主键或如果已存在实体主键,则直接返回该实体的主键
l CreateQuery(string,params ObjectParameter[])
从给定的查询字符串创建ObjectQuery对象。
l DeleteObject(object)
删除指定的实体对象
l Detach(object)
移除指定的实体对象
l ExecuteFunction(string,params ObjectParameter[])
对默认容器执行给定的函数。
l GetObjectByKey(System.Data.EntityKey key)
通过主键KEY从 ObjectStateManager 中检索对象(如果存在);否则从存储区中检索。
l Refresh(System.Data.Objects.RefreshMode refreshMode, object entity)
按指定持久更新模式,使用指定实体的存储区数据更新ObjectStateManager。。
l Refresh(System.Data.Objects.RefreshMode refreshMode, System.Collections.IEnumerable collection)
按指定持久处理模式,使用指定实体集的存储区数据更新ObjectStateManager。
l SaveChanges(bool )
将所有更新持久保存到存储区中。参数是客户端事务支持所需的参数。参数为true则在更新后自动将更改应用到ObjectStateManager中的实体。如果为false,则在更新后还需要调用AcceptAllChanges()以便更新ObjectStateManager中的实体。
l SaveChanges()
将所有更新持久保存到存储区中
l TryGetObjectByKey(System.Data.EntityKey,out object)
尝试从指定实体主键返回该实体
以上各个方法的具体用法,将在后面介绍。
接着,再看看有用的类ObjectQuery。
ObjectQuery有个有用的方法ToTraceString(),这个方法用于追踪所执行的SQL语句,通过此方法我们可以获取所执行的SQL语句,以便我们查看、分析具体执行的SQL语句。(类似Nhibernate配置文件中的showsql节)
再了解一下ObjectStateEntry。
ObjectStateEntry维护实体实例或关系实例的状态(已添加、已删除、已分离、已修改或未更改)、键值和原始值。还管理已修改属性的列表。其包含一下方法:
l AcceptChanges
接受当前值作为原始值,并将实体标记为Unchanged()。
l Delete
将实体标记为Deleted()。如果实体处于 Added()()() 状态,它将为Detached()。
l GetModifiedProperties
返回标记为Modified()的属性名称。
l SetModified
将状态设置为Modified()。
l SetModifiedProperty
将指定的属性标记为Modified()。
接着,再看看ObjectStateManager。
ObjectStateManager用于维护对象映射、对象状态/标识管理以及实体实例或关系实例的持久性。
l GetObjectStateEntries
获取给定EntityState的ObjectStateEntry集合。
l GetObjectStateEntry
获取给定的EntityKey 对应的ObjectStateEntry
现在,几个重要的类简单介绍完毕。后面,我们将具体学习它们的使用。
3--LINQ TO Entities
LINQ 技术(即LINQ to Entities)使开发人员能够通过使用LINQ 表达式和LINQ 标准查询运算符,直接从开发环境中针对实体框架对象上下文创建灵活的强类型查询。LINQ to Entities查询使用对象服务基础结构。ObjectContext 类是作为CLR 对象与实体数据模型进行交互的主要类。开发人员通过ObjectContext构造泛型ObjectQuery实例。ObjectQuery 泛型类表示一个查询,此查询返回一个由类型化实体组成的实例或集合。返回的实体对象可供更新并位于对象上下文中。以下是创建和执行LINQ to Entities查询的过程:
1.从ObjectContext构造ObjectQuery实例。
2.通过使用ObjectQuery实例在C#或Visual Basic中编写 LINQ to Entities查询。
3.将LINQ 标准查询运算符和表达式将转换为命令目录树。
4.对数据源执行命令目录树表示形式的查询。执行过程中在数据源上引发的任何异常都将直接向上传递到客户端。
5.将查询结果返回到客户端。
一、Linq To Entities简单查询
下面将介绍简单的Linq To Entities查询,相关的查询语法可以使用基于表达式或基于方法的语法。本节使用的TestDriver.Net配合Nunit2.4进行测试。
1,投影
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Objects;
using NUnit.Framework;
namespace NorthWindModel
{
[TestFixture]
public class TestEFModel
{
[Test]
public void Select()
{
using (var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery customers = edm.Customers;
IQueryable cust1 = from c in customers
select c;
Assert.Greater(cust1.Count(), 0);
//使用ObjectQuery类的ToTraceString()方法显示查询SQL语句
Console.WriteLine(customers.ToTraceString());
}
}
}
}
输出:
SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]
1 passed, 0 failed, 0 skipped, took 11.00 seconds (NUnit 2.4).
在上面的输出内容中,可以看到使用了ToTraceString()方法来输出具体的SQL语句。同时Nunit也输出相关的测试情况,请注意查询所花费的时间,以便我们进行查询速度的分析比较。
2,条件限制
using (var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery customers = edm.Customers;
IQueryable cust1 = from c in customers
where c.CustomerID == "ALFKI"
select c;
Assert.AreEqual (cust1.Count(), 1);
foreach (var c in cust1)
Console.WriteLine("CustomerID={0}", c.CustomerID);
//基于方法的查询语法
var cust2 = edm.Customers.Where(c => c.CustomerID == "ALFKI");
Assert.AreEqual (cust2.Count(), 1);
foreach (var c in cust2)
Console.WriteLine("CustomerID={0}", c.CustomerID);
}
3,排序和分页
在使用Skip和Take方法实现分页时,必须先对数据进行排序,否则将会抛异常。
using (var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery customers = edm.Customers;
IQueryable cust10 = (from c in customers
orderby c.CustomerID
select c).Skip(0).Take(10);
Assert.AreEqual (cust10.Count(), 10);
foreach (var c in cust10)
Console.WriteLine("CustomerID={0}", c.CustomerID);
//基于方法的查询语法
var cust = edm.Customers.OrderBy(c => c.CustomerID).Skip(0).Take(10);
Assert.AreEqual (cust.Count(), 10);
foreach (var c in cust)
Console.WriteLine("CustomerID={0}", c.CustomerID);
}
4,聚合
可使用的聚合运算符有Average、Count、Max、Min 和Sum。
using (var edm = new NorthwindEntities())
{
var maxuprice = edm.Products.Max(p => p.UnitPrice);
Console.WriteLine(maxuprice.Value);
}
5,连接
可以的连接有Join 和GroupJoin 方法。GroupJoin组联接等效于左外部联接,它返回第一个(左侧)数据源的每个元素(即使其他数据源中没有关联元素)。
using (var edm = new NorthwindEntities())
{
var query = from d in edm.Order_Details
join order in edm.Orders
on d.OrderID equals order.OrderID
select new
{
OrderId = order.OrderID,
ProductId = d.ProductID,
UnitPrice = d.UnitPrice
};
foreach (var q in query)
Console.WriteLine("{0},{1},{2}",q.OrderId,q.ProductId,q.UnitPrice);
}
其他一些方法等就不多说了,和Linq to SQl 基本上是一样的。
二、LINQ to Entities查询注意事项
l 排序信息丢失
如果在排序操作之后执行了任何其他操作,则不能保证这些附加操作中会保留排序结果。这些操作包括 Select 和Where 等。另外,采用表达式作为输入参数的 First 和FirstOrDefault 方法不保留顺序。
如下代码:并不能达到反序排序的效果
using (var edm = new NorthwindEntities())
{
IQueryable cc = edm.Customers.OrderByDescending(c => c.CustomerID).Where(c => c.Region != null ).Select(c => c);
foreach (var c in cc)
Console.WriteLine(c.CustomerID);
}
l 不支持无符号整数
由于实体框架不支持无符号整数,因此不支持在LINQ to Entities查询中指定无符号整数类型。如果指定无符号整数,则在查询表达式转换过程中会引发 NotSupportedException异常,并显示无法创建类型为“结束类型”的常量值。此上下文仅支持基元类型(“例如 Int32、String 和Guid”)。
如下将会报异常的代码:
using (var edm = new NorthwindEntities())
{
uint id = UInt32.Parse("123");
IQueryable produt = from p in edm.Products
where p.UnitPrice == id
select p.ProductName;
foreach (string name in produt)
Console.WriteLine(name);
}
上面的代码中,由于id是uint而不是Int32,String,Guid的标量类型,所以在执行到where p.UnitPrice ==id这个地方时,会报异常。
l 不支持引用非标量闭包
不支持在查询中引用非标量闭包(如实体)。在执行这类查询时,会引发 NotSupportedException 异常,并显示消息“无法创建类型为“结束类型”的常量值。此上下文中仅支持基元类型(‘如 Int32、String 和Guid’)
如下将会报异常的代码:
using (var edm = new NorthwindEntities())
{
Customers customer = edm.Customers.FirstOrDefault();
IQueryable cc = from c in edm.Customers
where c == customer
select c.ContactName;
foreach (string name in cc)
Console.WriteLine(name);
}
上面的代码中,由于customer是引用类型而不是Int32,String,Guid的标量类型,所以在执行到where c==customer这个地方时,会报异常。
4--Entity SQL
Entity SQl是ADO.NET实体框架提供的SQl类语言,用于支持实体数据模型(EDM)。Entity SQl可用于对象查询和使用EntityClient提供程序执行的查询。
l 关键字
Value关键字
ESQl提供了SELECT VALUE子句以跳过隐式行构造。SELECT VALUE子句中只能指定一项。在使用这样的子句时,将不会对SELECT子句中的项构造行包装器,并且可生成所要形状的集合,例如:SELECT VALUE it FROM NorthwindEntities.Customers as it
it关键字
it 出现在ESQl中, 查询对象的别名默认值 "it" 改成其他字符串,例如:
"SELECT VALUE it FROM NorthwindEntities.Customers as it " 。
l 注释:
Entity SQl 查询可以包含注释。注释行以两个短划线(--) 开头。
"SELECT VALUE it FROM NorthwindEntities.Customers as it -- this a comment "
l Select查询
例如:
SELECT VALUE it FROM NorthwindEntities.Customers as it
l 参数
参数是在esql之外定义的变量,每个参数都有名称和类型,参数名称在查询表达式中定义,并以@符号作为前缀。例如:
Select VALUE c from NorthwindEntities.Customers as c where c.CustomerID=@customerID
l 聚合
Enity SQL不支持 * ,所以esql不支持count(*),而是使用count(0),例如:
Select count(0) from NorthwindEntities.Customers
l 分页SKIP/LIMIT
可以通过在ORDER BY子句中使用SKIP 和 LIMIT子子句执行物理分页。若要以确定的方式执行物理分页,应使用SKIP 和 LIMIT。如果您只是希望以非确定的方式限制结果中的行数,则应使用TOP。TOP 和 SKIP/LIMIT是互斥的
使用SKIP/LIMIT分页,esql代码如下:
Select value c from NorthwindEntities.Customers as c order by c.CustomerID skip 0 limit 10
l TOP
SELECT子句可以在可选的ALl /DISTINCT 修饰符之后具有可选的TOP子子句。TOP子子句指定查询结果中将只返回第一组行。esql代码如下:
Select top(10) c.CustomerID from NorthwindEntities.Customers as c order by c.CustomerID
l NULL处理
Null文本与Entity SQl类型系统中的任何类型都兼容,可以使用cast进行类型转换,例如:
select cast(c.region as string) from NorthwindEntities.Customers as c order by c.CustomerID limit 10
其中, Nvarchar等可以成string,数字类型可以转成int32,其他的类型转换类似。如果无法完成转换,则将报异常。还有可以处理的方法有treat。
l 标识符
Entity SQl提供两种标识符:简单标识符和带引号的标识符
简单标识符:Entity SQl中的简单标识符是字母数字和下划线字符的序列。标识符的第一个字符必须是字母字符(a-z 或 A-Z)。
带引号的标识符:带引号的标识符是括在方括号([])中的任何字符序列。带中文的部分,请使用方括号包括起来,否则会报如下异常信息:“简单标识符“中文”只能包含基本拉丁字符。若要使用UNICODE 字符,请使用转义标识符”
正确的代码如下:
Select c.CustomerID as [中文字符] from NorthwindEntities.Customers as c order by c.CustomerID skip 0 limit 10
l ROW
Esql可使用row来构建匿名的结构类型的纪录。例如:
SELECT VALUE row(p.ProductID as ProductID,p.ProductName as ProductName) FROM NorthwindEntities.Products as p order by p.ProductID LIMIT 10
l Key
提取引用或实体表达式的键。如下esql语句,直接返回Customer表的主键:
string esql = "SELECT value key(c) FROM NorthwindEntities.Customers as c order by c.CustomerID LIMIT 10"
l CreateRef/ref/deref
CreateRef创建对实体集中的实体的引用。
ref返回对实体实例的引用,之后就可以当作实体来访问其属性,esql语句如下:
SELECT ref(c).CustomerID FROM NorthwindEntities.Customers as c order by c.CustomerID LIMIT 10
deref运算符取消引用一个引用值,并生成该取消引用的结果。
l CASE语句:
string esql = "using SqlServer;select case when len(trim(c.CustomerID))==0 then true else false end from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
l 运算符
Esql支持的运算符有:加+、减-、乘*、除/、取模%、-负号。Esql语句如下:
select 100/2 as OP from NorthwindEntities.Customers as c order by c.CustomerID limit 10
l 比较运算符
Esql支持的比较运算符有:=,>,>=,IS [NOT] NULl ,<,[NOT] BETWEEN,!=,<>,[NOT] LIKE。Esql语句如下:
select value p from NorthwindEntities.Products as p where p.UnitPrice > 20 order by p.ProductID limit 10
l 逻辑运算符
Esql支持的逻辑运算符有:and(&&),not(!),or(||)。Esql语句如下:
select value p from NorthwindEntities.Products as p where p.UnitPrice > 20 and p.UnitPrice<100 order by p.ProductID limit 10
或
select value p from NorthwindEntities.Products as p where p.UnitPrice > 20 && p.UnitPrice<100 order by p.ProductID limit 10
l 字符串连接运算符。
加号(+)是Entity SQl中可将字符串串联起来的唯一运算符。Esql语句如下:
select c.CustomerID + c.ContactName from NorthwindEntities.Customers as c order by c.CustomerID limit 10
l 嵌套查询
在Entity SQl中,嵌套查询必须括在括号中,将不保留嵌套查询的顺序
select c1.CustomerID from( select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10) as c1
l 日期时间函数
Esql提供的日期时间函数有:CurrentDateTime()获取当前服务器的日期时间,还有month,day,year,second, Minute ,Hour等。例如:
select CurrentDateTime() from NorthwindEntities.Customers as c order by c.CustomerID limit 10
l 字符串函数
Esql提供的字符串函数有:Concat,IndexOf,Left,Length,Ltrim,Replace,Reverse,Rtrim,SubString,Trim,ToLower,ToUpper.例如:
select Reverse(p.ProductName) as ProductName from NorthwindEntities.Products as p order by p.ProductID limit 10
l GUID
Esql提供newguid()函数,产生一个新的Guid。例如:
select newguid() from NorthwindEntities.Customers as c order by c.CustomerID limit 10
l 数学函数:
Abs,Ceiling,Floor,Round
l 统计函数:
Avg,BigCount,Count,Max,Min,StDev,Sum
l 位计算函数
如果提供 Null
BitWiseAnd,BitWiseNot,BitWiseOr,BitWiseXor
l 命名空间
Entity SQl引入命名空间以避免全局标识符(如类型名称、实体集、函数等)出现名称冲突。Entity SQl中的命名空间支持与.NET Framework中的命名空间支持类似。
Entity SQl提供两种形式的USING子句:限定命名空间(其中,提供较短的别名以表示命名空间)和非限定命名空间,如下例所示:
USING System.Data;
USING tsql = System.Data;
例如:
string esql = "using System; select cast(p.UnitPrice as Int32) from NorthwindEntities.Products as p order by p.ProductID limit 10 ";
string esql = "using System;using SqlServer; select(cast(p.UnitPrice as Int32)),SqlServer.ltrim(p.ProductName) as nameLen from NorthwindEntities.Products as p order by p.ProductID limit 10 ";
最后,简单说一下Esql与T-Sql的某些差异:
l Entity SQl中的所有列引用都必须用表别名限定.
l Esql不支持Any,all限定运算符以及*运算
l Entity SQl当前未提供对DMl语句(insert、update、delete)的支持。
l Entity SQl的当前版本未提供对DDL的支持。
5--ObjectQuery查询及方法
ObjectQuery 类支持对实体数据模型(EDM)执行LINQ to Entities和Entity SQl查询。ObjectQuery还实现了一组查询生成器方法,这些方法可用于按顺序构造等效于Entity SQl的查询命令。下面是ObjectQuery的查询生成器方法以及等效的Entity SQl语句:
Distinct,Except,GroupBy,Intersect,OfType,OrderBy,Select,SelectValue,Skip,Top,Union,UnionAll,Where
每个查询生成器方法返回ObjectQuery的一个新实例。使用这些方法可以构造查询,而查询的结果集基于前面ObjectQuery实例序列的操作。下面来看具体的代码片断:
l Execute方法:
using (var edm = new NorthwindEntities())
{
string esql = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query = edm.CreateQuery(esql );
ObjectResult results = query.Execute(MergeOption.NoTracking);
Assert.AreEqual (results.Count(), 10);
foreach (Customers c in query)
Console.WriteLine(c.CustomerID);
}
其中需要说明的是: MergeOption这个枚举类型的参数项,MergeOption有四种值分别是:
l AppendOnly:只追加新实体,不修改以前获取的现有实体。这是默认行为。
l OverwriteChanges:将 ObjectStateEntry 中的当前值替换为存储区中的值。这将使用服务器上的数据重写在本地所做的更改。
l PreserveChanges:将替换原始值,而不修改当前值。这对于在发生开放式并发异常之后强制成功保存本地值非常有用。
l NoTracking:将不修改 ObjectStateManager,不会获取与其他对象相关联的关系,可以改善性能。
l GetResultType方法:返回查询结果的类型信息.例如:
using (var edm = new NorthwindEntities())
{
string esql = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query = edm.CreateQuery(esql );
Console.WriteLine(query.GetResultType().ToString());
//输出结果为:
//NorthWindModel .Customers
}
l ToTraceString方法:获取当前执行的SQL语句。
l Where
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql = "select value c from NorthwindEntities.Customers as c ";
ObjectQuery query1 = edm.CreateQuery(esql );
//使用ObjectParameter的写法
query1 = query1.Where("it.CustomerId=@customerid");
query1.Parameters.Add(new ObjectParameter("customerid", "ALFKI"));
//也可以这样写
//ObjectQuery query2 = edm.Customers.Where("it.CustomerID=‘ALFKI‘");
foreach (var c in query1)
Console.WriteLine(c.CustomerID);
//显示查询执行的SQL语句
Console.WriteLine(query1.ToTraceString());
}
l First/ FirstOrDefault
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query = edm.CreateQuery(esql );
Customers c1 = query.First();
Customers c2 = query.FirstOrDefault();
Console.WriteLine(c1.CustomerID);
Assert.IsNotNull (c2);
Console.WriteLine(c2.CustomerID);
}
l Distinct
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql = "select value c.City from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query = edm.CreateQuery(esql );
query = query.Distinct();
foreach (string c in query)
{
Console.WriteLine("City {0}", c);
}
}
l Except:返回两个查询的差集。实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query1 = edm.CreateQuery(esql1);
string esql2 = "select value c from NorthwindEntities.Customers as c where c.Country=‘UK‘ order by c.CustomerID limit 10";
ObjectQuery query2 = edm.CreateQuery(esql2);
query1 = query1.Except(query2);
foreach (Customers c in query1)
{
Console.WriteLine(c.Country);
//输出:UK
}
}
l Intersect:返回两个查询的交集。实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query1 = edm.CreateQuery(esql1);
string esql2 = "select value c from NorthwindEntities.Customers as c where c.Country=‘UK‘ order by c.CustomerID limit 10";
ObjectQuery query2 = edm.CreateQuery(esql2);
query1 = query1.Intersect(query2);
foreach (Customers c in query1)
{
Console.WriteLine(c.Country);
}
}
l Union/UnionAll :返回两个查询的合集,包括重复项。其中UnionAll必须是相同类型或者是可以相互转换的。
l Include:可通过此方法查询出与相关的实体对象。实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c WHERE c.CustomerID =‘HANAR‘";
ObjectQuery query1 = edm.CreateQuery(esql1);
query1 = query1.Include("Orders");
foreach (Customers c in query1)
{
Console.WriteLine("{0},{1}", c.CustomerID, c.Orders.Count);
//输出:HANAR,14
}
}
l OfType:根据制定类筛选元素创建一个新的类型。此类型是要在实体模型中已定义过的。
l OrderBy
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query1 = edm.CreateQuery(esql1);
query1.OrderBy("it.country asc,it.city asc");
//也可以这样写
//query1.OrderBy("it.country asc");
//query1.OrderBy("it.city asc");
foreach (Customers c in query1)
{
Console.WriteLine("{0},{1}", c.Country, c.City);
}
}
l Select
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query1 = edm.CreateQuery(esql1);
ObjectQuery records = query1.Select("it.customerid,it.country");
foreach (DbDataRecord c in records)
{
Console.WriteLine("{0},{1}", c[0], c[1]);
}
Console.WriteLine(records.ToTraceString());
//SQL输出:
//SELECT TOP (10)
//1 AS [C1],
//[Extent1].[CustomerID] AS [CustomerID],
//[Extent1].[Country] AS [Country]
//FROM [dbo].[Customers] AS [Extent1]
//ORDER BY [Extent1].[CustomerID] ASC
}
l SelectValue
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery query1 = edm.CreateQuery(esql1);
ObjectQuery records = query1.SelectValue("it.customerid");
foreach (string c in records)
{
Console.WriteLine("{0}", c);
}
Console.WriteLine(records.ToTraceString());
//SQL输出:
//SELECT TOP (10)
//[Extent1].[CustomerID] AS [CustomerID]
//FROM [dbo].[Customers] AS [Extent1]
//ORDER BY [Extent1].[CustomerID] ASC
}
l Skip/Top
实例代码如下:
using (var edm = new NorthwindEntities())
{
string esql1 = "select value c from NorthwindEntities.Customers as c order by c.CustomerID ";
ObjectQuery query1 = edm.CreateQuery(esql1);
query1 = query1.Skip("it.customerid asc", "10");
query1 = query1.Top("10");
foreach (Customers c in query1)
{
Console.WriteLine("{0}", c.CustomerID);
}
Console.WriteLine(query1.ToTraceString());
//SQL输出:
//SELECT TOP (10)
//[Extent1].[CustomerID] AS [CustomerID]
//FROM [dbo].[Customers] AS [Extent1]
//ORDER BY [Extent1].[CustomerID] ASC
}
6--EntityClient
System.Data.EntityClient 命名空间是实体框架的.NET Framework数据提供程序。EntityClient 提供程序使用存储特定的ADO.NET数据提供程序类和映射元数据与实体数据模型进行交互。EntityClient首先将对概念性实体执行的操作转换为对物理数据源执行的操作。然后再将物理数据源返回的结果集转换为概念性实体。
EntityClient下的类有以下几个:
l EntityConnection
l EntityCommand
l EntityConnectionStringBuilder
l EntityParameter
l EntityDataReader
l EntityParameterCollection
l EntityProviderFactory
l EntityTransaction
从类的名字上看,我们就知道它们的作用是什么了。在此,就不再一一解释了。直接通过实例代码来学习它们。
l EntityConnection:
实例代码1:
string con = "name = NorthwindEntities";
using (EntityConnection econn = new EntityConnection(con))
{
string esql = "Select VALUE c from NorthwindEntities.Customers as c where c.CustomerID=‘ALFKI‘";
econn.Open();
EntityCommand ecmd = new EntityCommand(esql , econn);
EntityDataReader ereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
if (ereader.Read())
{
Console.WriteLine(ereader["CustomerID"]);
}
Console.WriteLine(ecmd.ToTraceString());
}
上述代码中,需要注意的是EntityConnection的构造方法。其中,连接字符串写法有多很,如下:
写法1:
string con ="name = NorthwindEntities" ;其中的”NorthwindEntities”是配置文件中的连接字符串名称
写法2:
string con = System.Configuration.ConfigurationManager.ConnectionStrings["NorthwindEntities"].ConnectionString; 其中的”NorthwindEntities”是配置文件中的连接字符串名称
写法3:
string con = @" metadata=res://*/NorthWind.csdl |res://*/NorthWind.ssdl |res://*/NorthWind.msl ;provider=System.Data.SqlClient;provider connection string=‘Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True‘";其中的
这些字符串是配置文件中的连接字符串的值
写法4:
NorthwindEntities edm = new NorthwindEntities();
string con = edm.Connection.ConnectionString;
上述写法中,基于写法简单、方便我比较推荐使用第1种或者第2种写法。
l EntityCommand
它具有的方法有:ExecuteDbDataReader、 ExecuteNonQuery 、 ExecuteReader 、 ExecuteScalar等。
实例代码2:
string con = "name = NorthwindEntities";
using (EntityConnection econn = new EntityConnection(con))
{
string esql = "Select VALUE c from NorthwindEntities.Customers as c where c.CustomerID=‘ALFKI‘";
econn.Open();
EntityCommand ecmd = econn.CreateCommand();
ecmd.CommandText = esql ;
EntityDataReader ereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
if (ereader.Read())
{
Console.WriteLine(ereader["CustomerID"]);
}
Console.WriteLine(ecmd.ToTraceString());
}
代码中,EntityCommand创建方式和实例代码1中的稍有不同,相信大家都明白,就不多说了。
l EntityConnectionStringBuilder
实例代码3:
EntityConnectionStringBuilder esb = new EntityConnectionStringBuilder();
esb.Provider = "System.Data.SqlClient";
esb.Metadata = http://www.mamicode.com/@"res://*/NorthWind.csdl |res://*/NorthWind.ssdl |res://*/NorthWind.msl ";
esb.ProviderConnectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True";
EntityConnection econn = new EntityConnection(esb.ConnectionString)//创建连接
l EntityParameter
代码实例4:
string con = "name = NorthwindEntities";
using (EntityConnection econn = new EntityConnection(con))
{
string esql = "Select value c from NorthwindEntities.Customers as c order by c.CustomerID skip @start limit @end";
econn.Open();
EntityCommand ecmd = new EntityCommand(esql , econn);
EntityParameter p1 = new EntityParameter("start", DbType.Int32);
p1.Value = http://www.mamicode.com/0;
EntityParameter p2 = new EntityParameter("end", DbType.Int32);
p2.Value = http://www.mamicode.com/10;
ecmd.Parameters.Add(p1);
ecmd.Parameters.Add(p2);
EntityDataReader ereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (ereader.Read())
{
Console.WriteLine(ereader["CustomerID"]);
}
Console.WriteLine(ecmd.ToTraceString());
}
其中,参数是以@符号前缀的,EntityParameter实体参数类,除了可以直接构造出实例来。为实体命令对象添加参数,我们还可以直接调用Parameters.AddWithValue方法。如下代码:
ecmd.Parameters.AddWithValue("start", 0);
ecmd.Parameters.AddWithValue("end", 10);
我比较喜欢用上面的代码,简单、方便。
l EntityDataReader
string con = "name = NorthwindEntities";
using (EntityConnection econn = new EntityConnection(con))
{
string esql = "Select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10 ";
econn.Open();
EntityCommand ecmd = new EntityCommand(esql , econn);
EntityDataReader ereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (ereader.Read())
{
Console.WriteLine("{0},{1},{2},{3},{4}", ereader[0], ereader[1], ereader[2], ereader[3], ereader[4]);
}
Console.WriteLine(ecmd.ToTraceString());
}
需要注意的是:CommandBehavior.SequentialAccess;这个地方。不同的枚举项,对查询会有不同影响。枚举如下:
l Default 此查询可能返回多个结果集。在功能上等效于调用 ExecuteReader()。
l SingleResult 查询返回一个结果集。
l SchemaOnly 查询仅返回列信息。
l KeyInfo 此查询返回列和主键信息。
l SingleRow 查询应返回一行。
l SequentialAccess 提供一种方法,以便 DataReader 处理包含带有大二进制值的列的行。
l CloseConnection 在执行该命令时,如果关闭关联的DataReader 对象,则关联的Connection 对象也将关闭。
需要说明的是,如果使用SequentialAccess则需按顺序访问列,否则将抛异常。如下代码,将会抛异常:
while (ereader.Read())
{
//异常信息:从列序列号“1”开始读取的尝试无效。通过CommandBehavior.SequentialAccess,只能从列序列号“5”或更大值处开始读取
Console.WriteLine("{0},{1},{2},{3},{4}", ereader[4], ereader[1], ereader[2], ereader[3], ereader[0]);
}
l EntityTransaction:
事务类。目前由于ESQL仅提供查询的命令,没有提供对Insert、Update、Delete等的支持。所以,我觉得目前这个类基本没有用,(不可能我做查询还使用事务吧!)。
从上述简单的介绍,我们可以看到,EntityClient和SqlClient下的类基本上是一致的。所以很容易掌握。其他就不多说了。
7--基本操作:增加、更新、删除、事务
本节,直接写通过代码来学习。这些基本操作都比较简单,与这些基本操作相关的内容在之前的1至6节基本介绍完毕。
l 增加:
方法1:使用AddToXXX(xxx)方法:实例代码如下:
using (var edm = new NorthwindEntities())
{
Customers c = new Customers { CustomerID = "c#", City = "成都市", Address = "中国四川省", CompanyName = "cnblogs", Country = "中国", Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府广场", ContactName = "风车车.Net" };
edm.AddToCustomers(c);
int result = edm.SaveChanges();
Assert.AreEqual (result, 1);
Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c#");
Console.WriteLine("CustomerId={0},City={1}", addc.CustomerID, addc.City);
}
方法2:使用ObjectContext的AddObject(string entitySetName, object entity)方法。实例代码如下:
using (var edm = new NorthwindEntities())
{
Customers c = new Customers { CustomerID = "c2", City = "成都市2", Address = "中国四川省2", CompanyName = "cnblogs", Country = "中国", Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府广场", ContactName = "风车车.Net" };
edm.AddObject("Customers", c);
int result = edm.SaveChanges();
Assert.AreEqual (result, 1);
Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Console.WriteLine("CustomerId={0},City={1}", addc.CustomerID, addc.City);
}
其中,在代码中,需要注意的是:AddObject方法中参数“entitySetName ”就是指对应实体名称,应该是:“Customers”,而不是“NorthwindEntities.Customers”;
l 更新:
using (var edm = new NorthwindEntities())
{
Customers addc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
addc.City = "CD";
addc.ContactName = "cnblogs";
addc.Country = "CN";
int result = edm.SaveChanges();
Assert.AreEqual (result, 1);
Customers updatec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Console.WriteLine("CustomerId={0},City={1}", updatec.CustomerID, updatec.City);
}
其中,需要注意的是:不能去更新主键,否则会报“System.InvalidOperationException : 属性“xxx”是对象的键信息的一部分,不能修改。”
l 删除:
实例代码如下:
using (var edm = new NorthwindEntities())
{
Customers deletec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
edm.DeleteObject(deletec);
int result = edm.SaveChanges();
Assert.AreEqual (result, 1);
Customers c = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Assert.AreEqual (c, null );
}
l 事务:
实例代码如下:
NorthwindEntities edm = null ;
System.Data.Common.DbTransaction tran = null ;
try
{
edm = new NorthwindEntities();
edm.Connection.Open();
tran = edm.Connection.BeginTransaction();
Customers cst = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c#");
cst.Country = "CN";
cst.City = "CD";
edm.SaveChanges();
tran.Commit();
}
catch (Exception ex)
{
if (tran != null )
tran.Rollback();
throw ex;
}
finally
{
if (edm != null && edm.Connection.State != System.Data.ConnectionState.Closed)
edm.Connection.Close();
}