Entity Framework 学习
Entity Framework 学习初级篇1--EF基本概况... 2
Entity Framework 学习初级篇2--ObjectContext、ObjectQuery、ObjectStateEntry、ObjectStateManager类的介绍... 7
Entity Framework 学习初级篇3-- LINQ TOEntities. 10
Entity Framework 学习初级篇4--EntitySQL. 17
Entity Framework 学习初级篇5--ObjectQuery查询及方法... 23
Entity Framework 学习初级篇6--EntityClient. 31
Entity Framework 学习初级篇7--基本操作:增加、更新、删除、事务... 37
Entity Framework 学习中级篇1—EF支持复杂类型的实现... 41
Entity Framework 学习中级篇2—存储过程(上). 47
Entity Framework 学习中级篇3—存储过程(中). 54
Entity Framework 学习中级篇4—存储过程(下). 61
Entity Framework 学习中级篇5—使EF支持Oracle9i67
Entity Framework 学习高级篇1—改善EF代码的方法(上)... 75
Entity Framework 学习高级篇2—改善EF代码的方法(下)... 81
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.NETEntity Data Model”项目,如下图所示:
名称取为“NorthWindEF.edmx”,然后点击“添加”。然后,在后面的步骤中,数据库选择“NorthWind”后,在选择影射对象是,把表、试图、存储过程全部都选上,其他的均保存默认的即可。最终生成的结果如下图所示。
好了,数据模型生成完毕。
最后,谈谈我认为的一些缺点:
· Edmx包含了所有对象的csdl,ssdl,msl文件,过于庞大,如果要手动修改这个文件,一不小心,眼睛看花了,就改错了。(和数据集一样的毛病)。
· 目前EF支持表、试图、存储过程,其他的对象不支持,而且对使用存储过程有很多限制(目前有EFExtension提供了更多对象的支持)。
· 除了MSSQL Server可直接提供这种可视化的设计界面外,其他的数据库目前还没有提供可视化设计界面(但可以自己来实现,后面介绍)。
· 性能问题。(网上看到有说比ADO.Net慢,又有人说比ADO.net快的,具体情况我还没测试过, 但我觉得像这个些类型的框架,性能肯定是比上原生态的ADO.net慢)
好了,接下来,学习以下简单的各种操作。
Entity Framework 学习初级篇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.IEntityWithKeyentity)
将带主键的实体对象附加到默认的容器中
l Attach(string,object)
将实体对象附加到指定的实体容器中
l CreateEntityKey(string,object)
给指定的实体对象创建实体主键或如果已存在实体主键,则直接返回该实体的主键
l CreateQuery<T>(string,paramsObjectParameter[])
从给定的查询字符串创建ObjectQuery对象。
l DeleteObject(object)
删除指定的实体对象
l Detach(object)
移除指定的实体对象
l ExecuteFunction<TElement>(string,paramsObjectParameter[])
对默认容器执行给定的函数。
l GetObjectByKey(System.Data.EntityKeykey)
通过主键KEY从 ObjectStateManager中检索对象(如果存在);否则从存储区中检索。
l Refresh(System.Data.Objects.RefreshMode refreshMode,object entity)
按指定持久更新模式,使用指定实体的存储区数据更新ObjectStateManager。。
l Refresh(System.Data.Objects.RefreshModerefreshMode, System.Collections.IEnumerable collection)
按指定持久处理模式,使用指定实体集的存储区数据更新ObjectStateManager。
l SaveChanges(bool)
将所有更新持久保存到存储区中。参数是客户端事务支持所需的参数。参数为true则在更新后自动将更改应用到ObjectStateManager中的实体。如果为false,则在更新后还需要调用AcceptAllChanges()以便更新ObjectStateManager中的实体。
l SaveChanges()
将所有更新持久保存到存储区中
l TryGetObjectByKey(System.Data.EntityKey,outobject)
尝试从指定实体主键返回该实体
以上各个方法的具体用法,将在后面介绍。
接着,再看看有用的类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用于维护对象映射、对象状态/标识管理以及实体实例或关系实例的持久性。
获取给定EntityState的ObjectStateEntry集合。
获取给定的 EntityKey 对应的 ObjectStateEntry
现在,几个重要的类简单介绍完毕。后面,我们将具体学习它们的使用。
Entity Framework 学习初级篇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, 投影
代码如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Data.Objects;
usingNUnit.Framework;
namespaceNorthWindModel
{
[TestFixture]
public classTestEFModel
{
[Test]
public void Select()
{
using(var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery<Customers> customers = edm.Customers;
IQueryable<Customers> cust1 = fromc 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 (NUnit2.4).
在上面的输出内容中,可以看到使用了ToTraceString()方法来输出具体的SQL语句。同时Nunit也输出相关的测试情况,请注意查询所花费的时间,以便我们进行查询速度的分析比较。
2, 条件限制
using(var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery<Customers> customers = edm.Customers;
IQueryable<Customers> cust1 = from c in customers
where c.CustomerID == "ALFKI"
select c;
Assert.AreEqual(cust1.Count(), 1);
foreach (varc in cust1)
Console.WriteLine("CustomerID={0}", c.CustomerID);
//基于方法的查询语法
var cust2 = edm.Customers.Where(c =>c.CustomerID == "ALFKI");
Assert.AreEqual(cust2.Count(), 1);
foreach (varc in cust2)
Console.WriteLine("CustomerID={0}", c.CustomerID);
}
3, 排序和分页
在使用Skip和Take方法实现分页时,必须先对数据进行排序,否则将会抛异常。
using(var edm = new NorthwindEntities())
{
//基于表达式的查询语法
ObjectQuery<Customers> customers = edm.Customers;
IQueryable<Customers> cust10 = (fromc in customers
orderby c.CustomerID
select c).Skip(0).Take(10);
Assert.AreEqual(cust10.Count(),10);
foreach(var c incust10)
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 (varedm = new NorthwindEntities())
{
varmaxuprice = 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
joinorder in edm.Orders
ond.OrderID equals order.OrderID
selectnew
{
OrderId =order.OrderID,
ProductId = d.ProductID,
UnitPrice = d.UnitPrice
};
foreach(var q inquery)
Console.WriteLine("{0},{1},{2}",q.OrderId,q.ProductId,q.UnitPrice);
}
其他一些方法等就不多说了,和Linq to SQL基本上是一样的。
二、LINQ to Entities 查询注意事项
l 排序信息丢失
如果在排序操作之后执行了任何其他操作,则不能保证这些附加操作中会保留排序结果。这些操作包括 Select 和 Where 等。另外,采用表达式作为输入参数的 First 和FirstOrDefault方法不保留顺序。
如下代码:并不能达到反序排序的效果
using (varedm = new NorthwindEntities())
{
IQueryable<Customers>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<string>produt = from p inedm.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<string>cc = from c inedm.Customers
where c == customer
select c.ContactName;
foreach (string name in cc)
Console.WriteLine(name);
}
上面的代码中,由于customer是引用类型而不是Int32,String,Guid的标量类型,所以在执行到where c==customer这个地方时,会报异常。
好,本节介绍完毕。后面将继续学习EF.
Entity Framework 学习初级篇4--Entity SQL
Entity SQL 是 ADO.NET 实体框架 提供的 SQL 类语言,用于支持 实体数据模型 (EDM)。Entity SQL 可用于对象查询和使用 EntityClient 提供程序执行的查询。
l 关键字
Value关键字
ESQL 提供了 SELECT VALUE 子句以跳过隐式行构造。SELECT VALUE 子句中只能指定一项。在使用这样的子句时,将不会对 SELECT 子句中的项构造行包装器,并且可生成所要形状的集合,例如:SELECTVALUE it FROM NorthwindEntities.Customers as it
it关键字
it 出现在 ESQL 中, 查询对象的别名默认值"it" 改成其他字符串,例如:
"SELECT VALUE it FROM NorthwindEntities.Customersas it " 。
l 注释:
Entity SQL 查询可以包含注释。注释行以两个短划线 (--) 开头。
"SELECT VALUE it FROMNorthwindEntities.Customers as it --this a comment "
l Select查询
例如:
SELECT VALUE it FROMNorthwindEntities.Customers as it
l 参数
参数是在esql之外定义的变量,每个参数都有名称和类型,参数名称在查询表达式中定义,并以@符号作为前缀。例如:
Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID=@customerID
l 聚合
Enity SQL不支持 * ,所以esql不支持count(*),而是使用count(0),例如:
Select count(0) fromNorthwindEntities.Customers
l 分页SKIP/LIMIT
可以通过在 ORDER BY 子句中使用 SKIP 和 LIMIT 子子句执行物理分页。若要以确定的方式执行物理分页,应使用 SKIP 和 LIMIT。如果您只是希望以非确定的方式限制结果中的行数,则应使用 TOP。TOP 和 SKIP/LIMIT 是互斥的
使用SKIP/LIMIT分页,esql代码如下:
Select value c fromNorthwindEntities.Customers as c order by c.CustomerID skip 0 limit 10
l TOP
SELECT 子句可以在可选的 ALL/DISTINCT 修饰符之后具有可选的 TOP 子子句。TOP 子子句指定查询结果中将只返回第一组行。esql代码如下:
Select top(10) c.CustomerID fromNorthwindEntities.Customers as c order by c.CustomerID
l NULL处理
Null 文本与 Entity SQL 类型系统中的任何类型都兼容,可以使用cast进行类型转换,例如:
select cast(c.region as string) fromNorthwindEntities.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.CustomerIDskip 0 limit 10
l ROW
Esql可使用row来构建匿名的结构类型的纪录。例如:
SELECT VALUE row(p.ProductID asProductID,p.ProductName as ProductName) FROM NorthwindEntities.Products as porder 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 FROMNorthwindEntities.Customers as c order by c.CustomerID LIMIT 10
deref运算符取消引用一个引用值,并生成该取消引用的结果。
l CASE语句:
string esql = "using SqlServer;select case whenlen(trim(c.CustomerID))==0 then true else false end from NorthwindEntities.Customers as c orderby c.CustomerID limit 10";
l 运算符
Esql支持的运算符有:加+、减-、乘*、除/、取模%、-负号。Esql语句如下:
select 100/2 as OP fromNorthwindEntities.Customers as c order by c.CustomerID limit 10
l 比较运算符
Esql支持的比较运算符有:=,>,>=,IS [NOT]NULL,<,[NOT] BETWEEN,!=,<>,[NOT] LIKE。Esql语句如下:
select value p fromNorthwindEntities.Products as p where p.UnitPrice > 20 order by p.ProductIDlimit 10
l 逻辑运算符
Esql支持的逻辑运算符有:and(&&),not(!),or(||)。Esql语句如下:
select value p fromNorthwindEntities.Products as p where p.UnitPrice > 20 and p.UnitPrice<100order by p.ProductID limit 10
或
select value p fromNorthwindEntities.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 fromNorthwindEntities.Customers as c orderby c.CustomerID limit 10
l 嵌套查询
在 Entity SQL 中,嵌套查询必须括在括号中,将不保留嵌套查询的顺序
select c1.CustomerID from( select valuec 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) asProductName 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 输入,则这些函数返回 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 asInt32) from NorthwindEntities.Productsas 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 fromNorthwindEntities.Products as p order byp.ProductID limit 10 ";
最后,简单说一下Esql与T-Sql的某些差异:
l Entity SQL 中的所有列引用都必须用表别名限定.
l Esql不支持Any,all限定运算符以及*运算
l Entity SQL 当前未提供对 DML 语句(insert、update、delete)的支持。
l Entity SQL 的当前版本未提供对 DDL 的支持。
Entity Framework 学习初级篇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())
{
stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);
ObjectResult<Customers> results = query.Execute(MergeOption.NoTracking);
Assert.AreEqual(results.Count(),10);
foreach(Customers c inquery)
Console.WriteLine(c.CustomerID);
}
其中需要说明的是: MergeOption这个枚举类型的参数项,MergeOption有四种值分别是:
l AppendOnly: 只追加新实体,不修改以前获取的现有实体。这是默认行为。
l OverwriteChanges: 将 ObjectStateEntry 中的当前值替换为存储区中的值。这将使用服务器上的数据重写在本地所做的更改。
l PreserveChanges: 将替换原始值,而不修改当前值。这对于在发生开放式并发异常之后强制成功保存本地值非常有用。
l NoTracking: 将不修改ObjectStateManager,不会获取与其他对象相关联的关系,可以改善性能。
l GetResultType方法:返回查询结果的类型信息.例如:
using(var edm = new NorthwindEntities())
{
stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);
Console.WriteLine(query.GetResultType().ToString());
//输出结果为:
//NorthWindModel.Customers
}
l ToTraceString方法:获取当前执行的SQL语句。
l Where
实例代码如下:
using(var edm = new NorthwindEntities())
{
stringesql = "select value c fromNorthwindEntities.Customers as c ";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql);
//使用ObjectParameter的写法
query1 = query1.Where("it.CustomerId=@customerid");
query1.Parameters.Add(new ObjectParameter("customerid", "ALFKI"));
//也可以这样写
//ObjectQuery<Customers>query2 = edm.Customers.Where("it.CustomerID='ALFKI'");
foreach(var c inquery1)
Console.WriteLine(c.CustomerID);
//显示查询执行的SQL语句
Console.WriteLine(query1.ToTraceString());
}
l First/ FirstOrDefault
实例代码如下:
using(var edm = new NorthwindEntities())
{
stringesql = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);
Customersc1 = query.First();
Customersc2 = query.FirstOrDefault();
Console.WriteLine(c1.CustomerID);
Assert.IsNotNull(c2);
Console.WriteLine(c2.CustomerID);
}
l Distinct
实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql = "select value c.City fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<string> query = edm.CreateQuery<string>(esql);
query = query.Distinct();
foreach(string c inquery)
{
Console.WriteLine("City {0}", c);
}
}
l Except:返回两个查询的差集。实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
stringesql2 = "select value c fromNorthwindEntities.Customers as c where c.Country='UK' order by c.CustomerID limit10";
ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(esql2);
query1 = query1.Except(query2);
foreach (Customers c inquery1)
{
Console.WriteLine(c.Country);
//输出:UK
}
}
l Intersect:返回两个查询的交集。实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
stringesql2 = "select value c from NorthwindEntities.Customersas c where c.Country='UK'order by c.CustomerID limit 10";
ObjectQuery<Customers> query2 = edm.CreateQuery<Customers>(esql2);
query1 =query1.Intersect(query2);
foreach(Customers c inquery1)
{
Console.WriteLine(c.Country);
}
}
l Union/UnionAll:返回两个查询的合集,包括重复项。其中UnionAll必须是相同类型或者是可以相互转换的。
l Include:可通过此方法查询出与相关的实体对象。实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c WHERE c.CustomerID ='HANAR'";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
query1 = query1.Include("Orders");
foreach(Customers c inquery1)
{
Console.WriteLine("{0},{1}", c.CustomerID,c.Orders.Count);
//输出:HANAR,14
}
}
l OfType: 根据制定类筛选元素创建一个新的类型。此类型是要在实体模型中已定义过的。
l OrderBy
实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
query1.OrderBy("it.country asc,it.city asc");
//也可以这样写
//query1.OrderBy("it.countryasc");
//query1.OrderBy("it.cityasc");
foreach(Customers c inquery1)
{
Console.WriteLine("{0},{1}", c.Country, c.City);
}
}
l Select
实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
ObjectQuery<DbDataRecord> records = query1.Select("it.customerid,it.country");
foreach(DbDataRecord c inrecords)
{
Console.WriteLine("{0},{1}", c[0], c[1]);
}
Console.WriteLine(records.ToTraceString());
//SQL输出:
//SELECTTOP (10)
//1AS [C1],
//[Extent1].[CustomerID]AS [CustomerID],
//[Extent1].[Country]AS [Country]
//FROM[dbo].[Customers] AS [Extent1]
//ORDERBY [Extent1].[CustomerID] ASC
}
l SelectValue
实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
ObjectQuery<string> records = query1.SelectValue<string>("it.customerid");
foreach(string c inrecords)
{
Console.WriteLine("{0}", c);
}
Console.WriteLine(records.ToTraceString());
//SQL输出:
//SELECTTOP (10)
//[Extent1].[CustomerID]AS [CustomerID]
//FROM[dbo].[Customers] AS [Extent1]
//ORDERBY [Extent1].[CustomerID] ASC
}
l Skip/Top
实例代码如下:
using (varedm = new NorthwindEntities())
{
stringesql1 = "select value c fromNorthwindEntities.Customers as c order by c.CustomerID ";
ObjectQuery<Customers> query1 = edm.CreateQuery<Customers>(esql1);
query1 = query1.Skip("it.customerid asc", "10");
query1 = query1.Top("10");
foreach(Customers c inquery1)
{
Console.WriteLine("{0}", c.CustomerID);
}
Console.WriteLine(query1.ToTraceString());
//SQL输出:
//SELECTTOP (10)
//[Extent1].[CustomerID]AS [CustomerID]
//FROM[dbo].[Customers] AS [Extent1]
//ORDERBY [Extent1].[CustomerID] ASC
}
本节,简单的介绍一下与ObjectQuery查询相关的语法,我个人觉得查询写法比较多,需要在日常的编程中去发现,在这里就不一一复述了。下节,将介绍EntityClient相关的内容。
Entity Framework 学习初级篇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:
stringcon = "name = NorthwindEntities";
using(EntityConnection econn = new EntityConnection(con))
{
stringesql = "Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID='ALFKI'";
econn.Open();
EntityCommandecmd = new EntityCommand(esql,econn);
EntityDataReaderereader = 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;providerconnection string='Data Source=.\SQLEXPRESS;InitialCatalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True'";其中的这些字符串是配置文件中的连接字符串的值
写法4:
NorthwindEntitiesedm = new NorthwindEntities();
string con =edm.Connection.ConnectionString;
上述写法中,基于写法简单、方便我比较推荐使用第1种或者第2种写法。
l EntityCommand
它具有的方法有:ExecuteDbDataReader、 ExecuteNonQuery 、 ExecuteReader 、 ExecuteScalar等。
实例代码2:
stringcon = "name = NorthwindEntities";
using(EntityConnection econn = new EntityConnection(con))
{
stringesql = "Select VALUE c fromNorthwindEntities.Customers as c where c.CustomerID='ALFKI'";
econn.Open();
EntityCommandecmd = econn.CreateCommand();
ecmd.CommandText = esql;
EntityDataReaderereader = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
if(ereader.Read())
{
Console.WriteLine(ereader["CustomerID"]);
}
Console.WriteLine(ecmd.ToTraceString());
}
代码中,EntityCommand创建方式和实例代码1中的稍有不同,相信大家都明白,就不多说了。
l EntityConnectionStringBuilder
实例代码3:
EntityConnectionStringBuilder esb = newEntityConnectionStringBuilder();
esb.Provider = "System.Data.SqlClient";
esb.Metadata = @"res://*/NorthWind.csdl|res://*/NorthWind.ssdl|res://*/NorthWind.msl";
esb.ProviderConnectionString = @"Data Source=.\SQLEXPRESS;InitialCatalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True";
EntityConnectioneconn = new EntityConnection(esb.ConnectionString)//创建连接
l EntityParameter
代码实例4:
stringcon = "name = NorthwindEntities";
using(EntityConnection econn = new EntityConnection(con))
{
stringesql = "Select value c fromNorthwindEntities.Customers as c order by c.CustomerID skip @start limit@end";
econn.Open();
EntityCommandecmd = new EntityCommand(esql,econn);
EntityParameterp1 = new EntityParameter("start", DbType.Int32);
p1.Value = 0;
EntityParameterp2 = new EntityParameter("end", DbType.Int32);
p2.Value = 10;
ecmd.Parameters.Add(p1);
ecmd.Parameters.Add(p2);
EntityDataReaderereader = 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
stringcon = "name = NorthwindEntities";
using(EntityConnection econn = new EntityConnection(con))
{
stringesql = "Select value c fromNorthwindEntities.Customers as c order by c.CustomerID limit 10 ";
econn.Open();
EntityCommandecmd = new EntityCommand(esql,econn);
EntityDataReaderereader = 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下的类基本上是一致的。所以很容易掌握。其他就不多说了。
Entity Framework 学习初级篇7--基本操作:增加、更新、删除、事务
本节,直接写通过代码来学习。这些基本操作都比较简单,与这些基本操作相关的内容在之前的1至6节基本介绍完毕。
l 增加:
方法1:使用AddToXXX(xxx)方法:实例代码如下:
using (var edm = new NorthwindEntities())
{
Customersc = new Customers{ CustomerID = "c#", City = "成都市",Address = "中国四川省", CompanyName= "cnblogs", Country = "中国",Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府广场",ContactName = "风车车.Net" };
edm.AddToCustomers(c);
intresult = edm.SaveChanges();
Assert.AreEqual(result,1);
Customersaddc = 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 (varedm = new NorthwindEntities())
{
Customersc = new Customers{ CustomerID = "c2", City = "成都市2",Address = "中国四川省2", CompanyName= "cnblogs", Country = "中国",Fax = "10086", Phone = "1008611", PostalCode = "610000", Region = "天府广场",ContactName = "风车车.Net" };
edm.AddObject("Customers", c);
intresult = edm.SaveChanges();
Assert.AreEqual(result,1);
Customersaddc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Console.WriteLine("CustomerId={0},City={1}",addc.CustomerID, addc.City);
}
其中,在代码中,需要注意的是:AddObject方法中参数“entitySetName ”就是指对应实体名称,应该是:“Customers”,而不是“NorthwindEntities.Customers”;
l 更新:
using (varedm = new NorthwindEntities())
{
Customersaddc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
addc.City = "CD";
addc.ContactName = "cnblogs";
addc.Country = "CN";
intresult = edm.SaveChanges();
Assert.AreEqual(result,1);
Customersupdatec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Console.WriteLine("CustomerId={0},City={1}",updatec.CustomerID, updatec.City);
}
其中,需要注意的是:不能去更新主键,否则会报“System.InvalidOperationException : 属性“xxx”是对象的键信息的一部分,不能修改。”
l 删除:
实例代码如下:
using (varedm = new NorthwindEntities())
{
Customersdeletec = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
edm.DeleteObject(deletec);
intresult = edm.SaveChanges();
Assert.AreEqual(result,1);
Customersc = edm.Customers.FirstOrDefault(cc => cc.CustomerID == "c2");
Assert.AreEqual(c,null);
}
l 事务:
实例代码如下:
NorthwindEntitiesedm = null;
System.Data.Common.DbTransaction tran = null;
try
{
edm = newNorthwindEntities();
edm.Connection.Open();
tran = edm.Connection.BeginTransaction();
Customerscst = 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();
throwex;
}
finally
{
if(edm != null && edm.Connection.State !=System.Data.ConnectionState.Closed)
edm.Connection.Close();
}
至此,初级篇基本介绍完毕。后面,打算写点,中级篇的东西。
Entity Framework 学习中级篇1—EF支持复杂类型的实现
本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作。
通常,复杂类型是指那些由几个简单的类型组合而成的类型。比如:一张Customer表,其中有FristName和LastName字段,那么对应的Customer实体类将会有FristName和LastName这两个属性。当我们想把FirstName和LastName合成一个名为CustomerName属性时,此时,如果要在EF中实现这个目的,那么我们就需要用到复杂类型。
目前,由于EF不能显示支持复杂类型,所以我们无法在VS里的可视化设计器里面来设计我们需要的复杂类型。所以,我们需要手动修改实体模型,以便使其支持复杂类型的属性。修改的主要步骤有以下几步:
l 产生实体模型
l 修改CSDL文件
l 修改msl文件
l 重新生成模型实体类
在后续的介绍,我使用数据库使用的是NorthWind,并针对Customer表对应的实体类来增加复杂属性Address,其中复杂属性Address由Address,City,Region,Country和PostalCode这个几个组合而成。
下面,介绍具体的操作步骤:
第一步:产生实体模型
实体模型的产生我们可以直接通过在VS可视化设计器来产生(如果不会,请参考《Entity Framework 学习初级篇1--EF基本概况》)。或者使用EdmGen工具来产生(EdmGen工具位于:系统盘符:\WINDOWS\Microsoft.NET\Framework\v3.5下面)。具体步骤就不复述了。
我产生的实体模型文件是:NorthwindEnites.edmx
第二步:修改csdl文件
产生了实体模型后,我们使用记事本或其他文本编辑工具打开实体模型,(小技巧:可以把实体模型后缀.edmx改为.xml,然后把实体模型文件直接拖到VS里面进行修改,这样修改起来比较方便,待修改完毕后,将后缀改回来即可。)
接着,开始手动修改csdl文件,找到模型文件中关于csdl定义的部分,然后找到实体类型名为Customers的定义节,删除原来的Address,City,Region,Country,PostalCode属性定义,然后添加一个名为Address的属性,如下代码所示:
<EntityTypeName="Customers">
<Key>
<PropertyRefName="CustomerID" />
</Key>
<PropertyName="CustomerID"Type="String"Nullable="false"MaxLength="5"Unicode="true"FixedLength="true" />
<PropertyName="CompanyName"Type="String"Nullable="false"MaxLength="40"Unicode="true"FixedLength="false" />
<PropertyName="ContactName"Type="String"MaxLength="30"Unicode="true"FixedLength="false" />
<PropertyName="ContactTitle"Type="String"MaxLength="30"Unicode="true"FixedLength="false" />
<PropertyName="Address"Type="NorthwindModel.CommonAddress"Nullable="false"></Property>
<PropertyName="Phone"Type="String"MaxLength="24"Unicode="true"FixedLength="false" />
<PropertyName="Fax"Type="String"MaxLength="24"Unicode="true"FixedLength="false" />
<NavigationPropertyName="Orders"Relationship="NorthwindModel.FK_Orders_Customers"FromRole="Customers"ToRole="Orders" />
<NavigationPropertyName="CustomerDemographics"Relationship="NorthwindModel.CustomerCustomerDemo"FromRole="Customers"ToRole="CustomerDemographics" />
</EntityType>
接着,需要添加一个名为CommonAddress复杂类型的定义,具体如下代码:
<ComplexTypeName="CommonAddress">
<PropertyName="Address"Type="String"MaxLength="60"Unicode="true"FixedLength="false" />
<PropertyName="City"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />
<PropertyName="Region"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />
<PropertyName="PostalCode"Type="String"MaxLength="10"Unicode="true"FixedLength="false" />
<PropertyName="Country"Type="String"MaxLength="15"Unicode="true"FixedLength="false" />
</ComplexType>
至此,csdl部分修改完毕。
第三步,修改msl文件:
找到msl部分的定义,修改Customers部分的影射定义。具体代码如下(请注意ComplexProperty节):
<EntitySetMappingName="Customers">
<EntityTypeMappingTypeName="IsTypeOf(NorthwindModel.Customers)">
<MappingFragmentStoreEntitySet="Customers">
<ScalarPropertyName="CustomerID"ColumnName="CustomerID" />
<ScalarPropertyName="CompanyName"ColumnName="CompanyName" />
<ScalarPropertyName="ContactName"ColumnName="ContactName" />
<ScalarPropertyName="ContactTitle"ColumnName="ContactTitle" />
<ComplexPropertyName="Address"TypeName="NorthwindModel.CommonAddress">
<ScalarPropertyName="Address"ColumnName="Address" />
<ScalarPropertyName="City"ColumnName="City" />
<ScalarPropertyName="Region"ColumnName="Region" />
<ScalarPropertyName="PostalCode"ColumnName="PostalCode" />
<ScalarPropertyName="Country"ColumnName="Country" />
</ComplexProperty>
<ScalarPropertyName="Phone"ColumnName="Phone" />
<ScalarPropertyName="Fax"ColumnName="Fax" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
至此,msl部分修改完毕
第四步:重新产生实体类文件。
我们可以使用EmdGen2工具来重新实体类.cs文件。具体操作如下:
将修改好的模型文件(edmx),拷贝到使用edmgen2.exe同目录下,然后在命令行中输入:
Edmgen2 /codegen cs NorthwindEnites.edmx
执行此命令后,会在当前的文件夹下生成一个NorthwindEnites.cs代码文件,也就是实体类的代码文件。将改文件改名为:NorthwindEnites.Designer.cs(这步主要是和edmx对应起来)。
然后,将NorthwindEnites.edmx和NorthwindEnites.Designer.cs文件添加到项目中。
至此,复合类型的修改完毕。
按照同样的修改过程,我们可以给Employees也增加一个Address的复杂类型属性。
接下来,我们看看具体使用代码:
l 查询:
[Test]
publicvoid TestAddress()
{
using(var db = newNorthwindModel.NorthwindEntities1())
{
Console.WriteLine("GetFive customer addresss :");
var cts = db.Customers.Take(5);
foreach (var c in cts)
{
Console.WriteLine("Address:{0},Country:{1},City:{2},PostalCode:{3}",c.Address.Address, c.Address.Country, c.Address.City, c.Address.PostalCode);
}
Console.WriteLine("GetFive Employess address:");
var emp = db.Customers.Take(5);
foreach(var c in emp)
{
Console.WriteLine("Address:{0},Country:{1},City:{2},PostalCode:{3}",c.Address.Address, c.Address.Country, c.Address.City, c.Address.PostalCode);
}
}
}
l 添加:
[Test]
publicvoid AddTest()
{
using(var db = newNorthwindModel.NorthwindEntities1())
{
var customer = newNorthwindModel.Customers
{
CustomerID = "2009",
CompanyName = "Complex Company",
ContactName = "xray2005",
Address = new NorthwindModel.CommonAddress
{
Address = "SiChuan,China",
City = "ChengDou",
Country = "China",
PostalCode ="610041",
Region = "Chenghua"
}
};
db.AddToCustomers(customer);
db.SaveChanges();
var cst = db.Customers.FirstOrDefault(c =>c.CustomerID == "2009");
Assert.IsNotNull(cst);
Console.WriteLine("CustomerID:{0},CompanyName:{1},ContactName:{2},City:{3},Country:{4}",cst.CustomerID, cst.CompanyName, cst.ContactName, cst.Address.City,cst.Address.Country);
}
}
l 条件查询:
[Test]
publicvoid QueryTest()
{
using(var db = newNorthwindModel.NorthwindEntities1())
{
var cst = db.Customers.FirstOrDefault(c =>c.Address.City == "ChengDou");
Assert.IsNotNull(cst);
Console.WriteLine("CustomerID:{0},CompanyName:{1},ContactName:{2},City:{3},Country:{4}",cst.CustomerID, cst.CompanyName, cst.ContactName, cst.Address.City,cst.Address.Country);
}
}
最后,补充说明:
1, 在VS的可视化设计器里,不支持复杂类型,所以修改后无法再在可视化设计器里修改模型(edmx文件)。
2, 复杂类型不能单独存在,它必须和某一实体相关起来。
3, 复杂类型不能包含导航属性,如导航到实体或实体集。
4, 复杂类型具有内部结构但没有 Key(主键) 属性的数据类型
下面是示例代码和EdmGen2工具的连接。
Entity Framework 学习中级篇2—存储过程(上)
目前,EF对存储过程的支持并不完善。存在以下问题:
l EF不支持存储过程返回多表联合查询的结果集。
l EF仅支持返回返回某个表的全部字段,以便转换成对应的实体。无法支持返回部分字段的情况。
l 虽然可以正常导入返回标量值的存储过程,但是却没有为我们自动生成相应的实体.cs代码,我们还是无法在代码中直接调用或使用标量存储过程
l EF不能直接支持存储过程中Output类型的参数。
l 其他一些问题。
下面,主要针对如何使用存储过程,以及存储返回实体、表的部分字段这个几个问题,做具体介绍。
l 导入存储过程及返回实体
在VS可视化设计器中,打开实体模型(emdx文件)。然后,鼠标右键点击“Customers”à “添加”à“函数导入”,然后选择“存储过程名称”,并输入函数导入名称,选择返回类型为实体并选择Customers。如下图所示:
之后,点击“确定”。之后,存储过程导入。在代码我们就可以使用改存储过程了。如下代码所示:
[Test]
public voidGetEntityBySP()
{
using (var db =new NorthwindEntities())
{
var cst = db.GetCustomerById("ALFKI").FirstOrDefault();
Assert.IsNotNull(cst);
Console.WriteLine("CustomerId:{0},ContactName:{1}",cst.CustomerID, cst.ContactName);
}
}
l 联合查询结果集的问题
在此版本的EF中,是不支持存储过程的多张表联合查询的,也就是说查询的结果集中,一部分字段来自表A,另一部分字段来自表B,像这种情况,目前EF无法直接进行处理。为此,可以通过写两个或多个存储过程来实现。比如:第一个存储过程返回表A的所有字段,第二存储过程返回表B的所有字段;然后,我们在代码中来实现联合的查询。
按照前面的思路,增加一个返回所有的Orders表字段的存储过程GetOrdersByCustomerId,再增加一个返回Order Details表全部字段的存储过程GetDetailsByCustomerId,并将它们导入到实体模型中。
其中,GetOrdersByCustomerId存储过程如下:
CREATE PROCEDURE GetOrdersByCustomerId
@CustomerId varchar(5)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM orders WHEREorders.CustomerID=@CustomerId;
END
GetDetailsByCustomerId存储过程如下:
CREATE PROCEDURE GetDetailsByCustomerId
@CustomerIdvarchar(5)
AS
BEGIN
SET NOCOUNT ON;
SELECT d.*
FROM Orders o,[Order Details] d
WHERE o.OrderId=d.OrderId AND o.CustomerId=@CustomerId;
END
之后,在我们的代码来实现联合查询。代码如下:
[Test]
public voidGetOrderBySp()
{
using (var db =new NorthwindEntities())
{
var orders = db.GetOrdersByCustomerId("VINET").ToList();
var details = db.GetDetailsByCustomerId("VINET").ToList();
orders.ForEach(o =>o.Order_Details.Attach(details.Where(d => d.OrderID == o.OrderID)));
foreach (varorder in orders)
{
Console.WriteLine(order.OrderID);
foreach (var d in order.Order_Details)
Console.WriteLine(d.ProductID);
}
}
}
其中,需要注意的,由于是分别执行了两个存储,在内存中是以两个对立的对象存在,它们之前是没有建立联系的。为此,我们需要使用Attach方法来把他们联系起来(红色代码段),这样我们就可以通过导航来访问对象的实体了,也就是foreach (var d in order.Order_Details)中的order.Order_Details。
l 返回部分字段的问题
默认情况,目前此版本的EF在使用存储过程返回实体的时候,必须返回所有的字段,以便是EF能够自动将返回的结果转换成对应的实体。否则会报“数据读取器与指定的XXX类型不兼容的异常,….”。
接下来,我们通过建立一个存储过程,并建立新建立一个实体来解决此问题。首先,我们在数据库中建立一个名为GetCustomerAndOrders的存储过程,其定义如下:
CREATE PROCEDURE GetCustomerAndOrders
AS
BEGIN
SET NOCOUNT ON;
SELECTc.CustomerID,c.CompanyName,o.OrderID,o.OrderDate,d.Quantity
FROMCustomers c,Orders o,[OrderDetails] d
WHEREc.CustomerID=o.CustomerID AND o.OrderID=d.OrderID;
END
GO
然后,添加一个实体CustomerOders,并设置属性如下图所示:
然后,在VS可视化设计器中,打开实体模型(emdx文件),通过添加à函数导入,导入存储过程GetCustomerAndOrders,并取名为GetCustomerAndOrders,返回类型设置为实体CustomerOrders。
最后,我们就可以代码实体此实体和存储过程了。如下代码:
[Test]
public voidGetOrderCountByCustomerId()
{
using (vardb = new NorthwindEntities())
{
var co =db.GetCustomerAndOrders().Take(10).Skip(0);
foreach(var c in co)
Console.WriteLine(c.CustomerID);
}
}
l 返回标量值问题
目前,EF对存储过程返回标量值的支持并没有完全。虽然,我们可以按照前面的步骤导入函数,并设置返回标量值的类型,同时EF会在实体模型文件为我们自动生成此存储过程相关的映射配置等。但是,EF却没有为我们生成在实体模型cs文件中,自动生成相应的.cs代码,所以我们根本无法直接调用此存储过程。为解决此问题,我们需要手动添加代码到实体模型的.cs代码文件中。
首先,在数据库中建立存储存储过程GetOrderCntByCustomerId,代码如下:
CREATE PROCEDUREGetOrderCntByCustomerId
@CustomerId varchar(5)
AS
BEGIN
SET NOCOUNT ON;
SELECT count(*) FROM Orders WHERE Orders.CustomerId=@CustomerId;
END
接着,按照正常的步骤,导入此存储过程并设置返回类型为标量类型的Int32。
然后,我们需要添加一个泛型的方法和一个执行存储过程的方法,代码如下:
public partial class NorthwindEntities
{
//泛型方法用于执行标量存储过程
private T ExecuteFunction<T>(string functionName, System.Data.EntityClient.EntityParameter[] parameters) where T : struct
{
System.Data.EntityClient.EntityCommand cmd =((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddRange(parameters);
cmd.CommandText = this.DefaultContainerName + "." + functionName;
try
{
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
var obj = cmd.ExecuteScalar();
return (T)obj;
}
catch (System.Exception)
{
throw;
}
finally
{
cmd.Connection.Close();
}
}
//执行数据库中GetOrderCntByCustomerId存储过程的方法
public intGetOrderCountByCustomerId(string CustomerId)
{
var param = newSystem.Data.EntityClient.EntityParameter("CustomerId", System.Data.DbType.String);
param.Value = CustomerId;
return ExecuteFunction<int>("GetOrderCountByCustomerId",new[] { param });
}
}
最后,通过以上修改,我们就可以直接使用返回标量值的存储过程,代码如下:
[Test]
public voidGetOrderCountByCustomerId()
{
using (var db =new NorthwindEntities())
{
var result = db.GetOrderCountByCustomerId("VINET");
Assert.Greater(result, 0);
Console.WriteLine(result.ToString());
}
}
至此,我们就解决了EF存储过程返回标量的问题。
Entity Framework 学习中级篇3—存储过程(中)
目前,EF对存储过程的支持并不完善。存在以下问题:
l EF不支持存储过程返回多表联合查询的结果集。
l EF仅支持返回返回某个表的全部字段,以便转换成对应的实体。无法支持返回部分字段的情况。
l 虽然可以正常导入返回标量值的存储过程,但是却没有为我们自动生成相应的实体.cs代码,我们还是无法在代码中直接调用或使用标量存储过程
l EF不能直接支持存储过程中Output类型的参数。
l 其他一些问题。
本节,我们将学习如何手动添加/修改存储过程,如何使EF能够支持Output类型的参数。
l 添加/修改存储过程
有时候,某个SQL语句比较复杂,但是数据库中又没有定义相应的存储过程。这个时候,我们又想使上层代码比较简单、方便的方式来完成此项任务。那么,此时,我们便可以手工在实体模型(.edmx文件)中添加自己需要的存储过程了。这样既方便上层调用又方便后期的修改。
以手动修改实体模型edmx文件,添加名为CustomerByCommandText的存储过程为例。具体步骤如下:
修改实体模型文件,找到ssdl部分,添加如下代码:
<Function Name="CustomerByCommandText" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo" >
<CommandText>
select c.* from Customers c,Orders o where c.CustomerID=o.CustomerID
</CommandText>
</Function>
然后,再找到csdl部分,添加如下代码:
<FunctionImport Name="CustomerByCommandText" EntitySet="Customers" ReturnType="Collection(NorthwindModel.Customers)"></FunctionImport>
接着,找到msl部分,添加如下代码:
<FunctionImportMapping FunctionImportName="CustomerByCommandText" FunctionName="NorthwindModel.Store.CustomerByCommandText"/>
最后,在实体模型的.cs文件里面,添加一个执行此存储过程的方法,代码如下:
public global::System.Data.Objects.ObjectResult<Customers>GetCustomerByCommandText()
{
return base.ExecuteFunction<Customers>("CustomerByCommandText");
}
至此,修改完毕。
现在,我们就可以在代码使用刚才手工定义的存储过程了。如下代码所示:
[Test]
public voidGetCustomerByCmdText()
{
using (var db =new NorthwindEntities())
{
var csts =db.GetCustomerByCommandText().Take(10).Skip(0);
foreach (var c in csts)
Console.WriteLine(c.CustomerID);
}
}
其实,关键的地方就是CommandText这个部分的内容,它里面就是要执行的SQL语句。另外,我们可以在修改实体模型emdx文件的同时,我们可以看到所有的实体类查询的SQL语句命令都可以在edmx文件里找到,我们都可以进行相应的修改。
l Output类型参数
在实际应用当中,很多时候,我们需要使用output类型的存储过程参数,以便返回我们需要的值。但是,目前,EF不能直接支持output类型的存储过程参数。为此,我们需要对实体模型进行修改,以便使其支持output类型的输出参数。具体过程如下:
在数据库中建立一个为名的GetNameByCustomerId存储过程,代码如下:
CREATE PROCEDURE GetNameByCustomerId
@CustomerId varchar(5),
@ContactName varchar(30) output
AS
BEGIN
SET NOCOUNT ON;
SELECT @ContactName=ContactName
FROM Customers
WHERE CustomerID=@CustomerId;
END
然后,开始修改实体模型edmx文件。
先找到ssdl定义的部分,添加如下代码:
<Function Name="GetNameByCustomerId" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="CustomerId" Type="varchar" Mode="In" MaxLength="5"></Parameter>
<Parameter Name="ContactName" Type="varchar" Mode="Out" MaxLength="30"></Parameter>
</Function>
接着,在找到csdl定义的部分,添加如下代码:
<FunctionImport Name="GetNameByCustomerId">
<Parameter Name="CustomerId" Mode="In" Type="String" MaxLength="5"></Parameter>
<Parameter Name="ContactName" Mode="Out" Type="String" MaxLength="30"></Parameter>
</FunctionImport>
最后,找到msl定义的部分,添加如下代码:
<FunctionImportMapping FunctionImportName="GetNameByCustomerId" FunctionName="NorthwindModel.Store.GetNameByCustomerId"></FunctionImportMapping>
至此,实体模型emdx文件修改完毕。
接下来,我们需要在实体模型的.cs文件中,增加相应的调用方法。代码如下:
public partial class NorthwindEntities1
{
//执行GetNameByCustomerId的方法
public voidGetNameByCustomerId(string CustomerId, out stringContactName)
{
ContactName= string.Empty;
var Pars = newSystem.Data.EntityClient.EntityParameter[]
{
new System.Data.EntityClient.EntityParameter{ ParameterName="CustomerId", DbType=System.Data.DbType.String,Value=CustomerId},
new System.Data.EntityClient.EntityParameter{ParameterName="ContactName", DbType=System.Data.DbType.String, Direction=System.Data.ParameterDirection.Output}
};
this.ExecuteNonQuery("GetNameByCustomerId",Pars);
ContactName = Pars[1].Value.ToString();
}
//辅助方法,执行SQL命令
private voidExecuteNonQuery(string functionName,System.Data.EntityClient.EntityParameter[]parameters)
{
System.Data.EntityClient.EntityCommand cmd =((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddRange(parameters);
cmd.CommandText = this.DefaultContainerName + "." + functionName;
try
{
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
catch (System.Exception)
{
throw;
}
finally
{
cmd.Connection.Close();
}
}
}
现在,所有的修改工作都做完了。接下来,我们就可以在代码中直接调用此存储过程了。示例代码如下:
[Test]
public voidOutputTest()
{
using (var db =new NorthwindModel.NorthwindEntities1())
{
string contactname = string.Empty;
db.GetNameByCustomerId("ALFKI", out contactname);
Assert.IsTrue(!string.IsNullOrEmpty(contactname));
Console.WriteLine(contactname);
}
}
至此,我们便可以使用Output类型的输出参数了。
Entity Framework 学习中级篇4—存储过程(下)
在EF中,各个实体的插入、更新和删除也都通过使用存储过程来完成,以便提高点性能。这个类似于数据集。其步骤是:先定义存储过程,然后在VS的可视化设计器,设置存储过程映射即可。
下面,以为Supplier实体映射存储过程为例。
分别建立插入、更新和删除存储过程。
InsertSuppliers存储过程定义如下:
CREATE PROCEDURE [dbo].[InsertSuppliers]
-- Add theparameters for the stored procedure here
@CompanyName nvarchar(40),
@ContactName nvarchar(30),
@ContactTitle nvarchar(30),
@Address nvarchar(60),
@City nvarchar(15),
@Region nvarchar(15),
@PostalCode nvarchar(10),
@Country nvarchar(15),
@Phone nvarchar(24),
@Fax nvarchar(24),
@HomePage ntext
AS
BEGIN
-- SETNOCOUNT ON added to prevent extra result sets from
--interfering with SELECT statements.
SET NOCOUNT ON;
INSERTINTO Suppliers(CompanyName,ContactName,ContactTitle,Address,City,Region,PostalCode,Country,Phone,Fax,HomePage)
VALUES(@CompanyName,@ContactName,@ContactTitle,@Address,@City,@Region,@PostalCode,@Country,@Phone,@Fax,@HomePage);
SELECTSCOPE_IDENTITY()AS SupplierID;
END
DeleteSuppliers存储过程定义如下:
CREATE PROCEDURE [dbo].[DeleteSuppliers]
-- Add theparameters for the stored procedure here
@SupplierID int
AS
BEGIN
-- SETNOCOUNT ON added to prevent extra result sets from
--interfering with SELECT statements.
SET NOCOUNT ON;
DELETESuppliers WHERE SupplierID=@SupplierID
END
UpdateSuppliers存储过程定义如下:
CREATE PROCEDURE [dbo].[UpdateSuppliers]
-- Add the parameters forthe stored procedure here
@SupplierID int,
@CompanyName nvarchar(40),
@ContactName nvarchar(30),
@ContactTitle nvarchar(30),
@Address nvarchar(60),
@City nvarchar(15),
@Region nvarchar(15),
@PostalCode nvarchar(10),
@Country nvarchar(15),
@Phone nvarchar(24),
@Fax nvarchar(24),
@HomePage ntext
AS
BEGIN
-- SET NOCOUNT ON addedto prevent extra result sets from
-- interfering withSELECT statements.
SET NOCOUNT ON;
UPDATESuppliers SET CompanyName=@CompanyName,ContactName=@ContactName,ContactTitle=@ContactTitle,Address=@Address,City=@City,Region=@Region,PostalCode=@PostalCode,Country=@Country,Phone=@Phone,Fax=@Fax,HomePage=@HomePage
WHERE SupplierID=@SupplierID
END
然后,在VS可视化设计器中,打开实体模式edmx文件,然后在Suppliers实体上鼠标右键à“存储过程映射”,然后在在分别设置存储过程即可。如下图所示:
至此,存储过程映射完毕。我们下面来具体的使用,代码如下:
使用存储过程Insert:
[Test]
public voidSPInsert()
{
using (var db =new NorthwindEntities1())
{
var supplier = newSuppliers();
supplier.CompanyName = "cnblogs";
supplier.ContactName = "xray2005";
supplier.ContactTitle = "net";
supplier.City = "成都";
supplier.Region = "四川";
supplier.Country = "中国";
supplier.PostalCode = "600041";
supplier.Phone = "028-8001";
supplier.Fax = "028-8002";
supplier.HomePage = "http://xray2005.cnblogs.com";
db.AddToSuppliers(supplier);
db.SaveChanges();
}
}
使用存储过程Update:
[Test]
public voidSPUpdate()
{
using (var db =new NorthwindEntities1())
{
var supplier = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 30);
Assert.IsNotNull(supplier);
supplier.CompanyName = "CNBLOGS";
supplier.ContactName = "xray2005";
supplier.ContactTitle= "♂风风车.net";
supplier.City = "成都";
supplier.Region = "四川";
supplier.Country = "China";
supplier.PostalCode = "600040";
supplier.Phone = "028-1008611";
supplier.Fax = "028-10086";
supplier.HomePage = "http://www.cnblogs.com/xray2005";
db.SaveChanges();
}
}
使用存储过程Delete:
[Test]
public voidSPDelete()
{
using(var db = new NorthwindEntities1())
{
var supplier = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 31);
Assert.IsNotNull(supplier);
db.DeleteObject(supplier);
db.SaveChanges();
var supplier1 = db.Suppliers.FirstOrDefault(s=> s.SupplierID == 31);
Assert.IsNull(supplier1);
}
}
至此,实体存储过程映射介绍完毕。本节,内容比较简单。
Entity Framework 学习中级篇5—使EF支持Oracle9i
从Code MSDN上下载下来的EFOracleProvider不支持Oracle9i.但是,目前我所使用的还是Oracle9i。为此,对EFOracleProvider修改了以下,以便使其支持Oracle9i.
下面说说具体修改地方.(红色部分为添加或修改的代码部分)
一,修改EFOracleProvider
1,修改EFOracleProviderManifest.cs类文件,
internal const string TokenOracle9i ="9i";//addby xray2005
internal const string TokenOracle10g = "10g";
internal const string TokenOracle11g = "11g";
以下两个地方,不修改也是可以的.但考虑目前我主要是使用9i,所以也就修改成9i了.
private EFOracleVersion_version = EFOracleVersion.Oracle9i; //EFOracleVersion.Oracle11g;
private string _token= TokenOracle9i; //TokenOracle10g;
2,修改EFOracleVersion.cs类文件,如下代码所示:
namespace EFOracleProvider
{
usingSystem;
///<summary>
/// This enum describes the current storage version
///</summary>
internalenum EFOracleVersion
{
Oracle9i = 9, //addby xray2005
/// < summary>
/// Oracle10g
/// < /summary>
Oracle10g = 10,
/// < summary>
/// Oracle 11g
/// < /summary>
Oracle11g = 11,
// higher versions go here
}
///<summary>
/// This class is a simple utility class that determines theversion from the
/// connection
///</summary>
internalstatic class EFOracleVersionUtils
{
/// < summary>
/// Get the version fromthe connection.
/// < /summary>
/// < paramname="connection">currentconnection</param>
/// < returns>version for the current connection</returns>
internal static EFOracleVersion GetStorageVersion(EFOracleConnection connection)
{
string serverVersion =connection.ServerVersion;
if (serverVersion.StartsWith("9."))
{
return EFOracleVersion.Oracle9i; //add by xray2005
}
else if(serverVersion.StartsWith("10."))
{
return EFOracleVersion.Oracle10g;
}
else if(serverVersion.StartsWith("11."))
{
return EFOracleVersion.Oracle11g;
}
throw new ArgumentException("Couldnot determine storage version; " +
"a valid storage connection or a version hintis required.");
}
internal static string GetVersionHint(EFOracleVersionversion)
{
switch (version)
{
case EFOracleVersion.Oracle9i:
return EFOracleProviderManifest.TokenOracle9i; //add by xray2005
case EFOracleVersion.Oracle10g:
return EFOracleProviderManifest.TokenOracle10g;
case EFOracleVersion.Oracle11g:
return EFOracleProviderManifest.TokenOracle11g;
default:
throw new ArgumentException("Couldnot determine storage version; " +
"a valid storage connection or a version hintis required.");
}
}
internal static EFOracleVersion GetStorageVersion(string versionHint)
{
if (!string.IsNullOrEmpty(versionHint))
{
switch (versionHint)
{
case EFOracleProviderManifest.TokenOracle9i:
return EFOracleVersion.Oracle9i; //add by xray2005
case EFOracleProviderManifest.TokenOracle10g:
return EFOracleVersion.Oracle10g;
case EFOracleProviderManifest.TokenOracle11g:
return EFOracleVersion.Oracle11g;
}
}
throw new ArgumentException("Couldnot determine storage version; " +
"a valid storage connection or a version hintis required.");
}
internal static bool IsVersionX(EFOracleVersionstorageVersion)
{
return storageVersion == EFOracleVersion.Oracle9i ||storageVersion == EFOracleVersion.Oracle10g ||
storageVersion == EFOracleVersion.Oracle11g; //add by xray2005
}
}
}
二,使用EFOracleProvider
修改完毕后,编译一下.如果是自己下载的源代码编译的,那么编译后的EFOracleProvider自动已经在GAC注册了.如果是手动注册EFOracleProvider到GAC,那么命令如下:
gacutil –I “EFOracleProvider.dll”
其中gacutil.exe位于:系统盘符号:\Program Files\Microsoft SDKs\Windows\v6.0A\bin下面。
接下来,我们需要做的就是,把这个EFOracleProvider添加到Machine.config中.
第一步,找到Machine.config文件.该文件的位置在:
系统盘符号:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG下面.
第二步,用打开Machine.config文件,在DbProviderFactories配置节点,增加EFOracleProvider的配置,如下所示:
<DbProviderFactories>
<add name="OdbcData Provider" invariant="System.Data.Odbc"description=".Net Framework Data Provider for Odbc"type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<addname="OleDb Data Provider" invariant="System.Data.OleDb"description=".Net Framework Data Provider for OleDb"type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<addname="OracleClient Data Provider"invariant="System.Data.OracleClient" description=".Net FrameworkData Provider for Oracle"type="System.Data.OracleClient.OracleClientFactory,System.Data.OracleClient, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add name="SqlClientData Provider" invariant="System.Data.SqlClient"description=".Net Framework Data Provider for SqlServer"type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<addname="Microsoft SQL Server Compact Data Provider"invariant="System.Data.SqlServerCe.3.5" description=".NETFramework Data Provider for Microsoft SQL Server Compact"type="System.Data.SqlServerCe.SqlCeProviderFactory,System.Data.SqlServerCe, Version=3.5.0.0,Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
<addname="EF Oracle Data Provider" invariant="EFOracleProvider"description="EF Provider for Oracle"type="EFOracleProvider.EFOracleProviderFactory,EFOracleProvider, Version=1.0.0.0,Culture=neutral, PublicKeyToken=def642f226e0e59b"/>
</DbProviderFactories>
第三步,保存即可.
接下来,简单的介绍一下,如何使用这个EFOralceProvider.
第1步:在命令行窗口,将目录定位到提示符, 系统盘符:\WINDOWS\Microsoft.NET\Framework\v3.5.如下所示(是我电脑上的目录):
第2步,输入相应的生成参数.如下图所示:
将“data source=test;user id=xray;password=123”成你自己的对应的参数即可.
确定之后,就可以看到生成的结果了,同时会有写信息出来,如下示:
至此,EdmGen就为我们生成需要的文件.生成的文件如下所示:
l TestEFModel.csdl
l TestEFModel.msl
l TestEFModel.ssdl
l TestEFModel.ObjectLayer.cs
l TestEFModel.Views.cs
然后,通过EdmGen2工具, 使用刚刚生成的TestEFModel .csdl, TestEFModel .msl, TestEFModel .ssdl三个文件来生成一个模型.
命令如下:
Edmgen2.exe /toedmx TestEFModel.csdlTestEFModel.msl TestEFModel.ssdl
确定之后,该工具就会为我们生成一个TestEFModel.edmx文件了.
然后,把这个文件加入到我们的项目中,同时修改项目的App.Config文件连接字符串,如下所示:
<?xmlversion="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="NorthwindEFModelContext"
connectionString="provider=EFOracleProvider;
metadata=res://*/TestEFModel.csdl|res://*/TestEFModel.ssdl|res://*/TestEFModel.msl;
Provider Connection String='data source=test;user id=xray;password=1111'"
providerName="System.Data.EntityClient"/>
</connectionStrings>
</configuration>
最后保存.
至此,修改EFOracleProvider并使用,介绍完毕。
最后提供几个连接,以方便大家学习研究:
l Code.MSDN上的EFOracleProvider;
l 经过我修改后,支持Oracle9i的EFOracleProvider.dll
Entity Framework 学习高级篇1—改善EF代码的方法(上)
本节,我们将介绍一些改善EF代码的相关方法,如NoTracking,GetObjectByKey, Include等。
l MergeOption.NoTracking
当我们只需要读取某些数据而不需要删除、更新的时候,可以指定使用MergeOption.NoTracking的方式来执行只读查询(EF默认的方式是AppendOnly)。当指定使用NoTracking来进行只读查询时,与实体相关的引用实体不会被返回,它们会被自动设置为null。因此,使用NoTracking可以提升查询的性能。示例代码如下:
[Test]
public voidNoTrackingTest()
{
using (var db =new NorthwindEntities1())
{
//针对Customers查询将使用MergeOption.NoTracking
db.Customers.MergeOption = MergeOption.NoTracking;
var cust = db.Customers.Where(c => c.City =="London");
foreach (var c in cust)
Console.WriteLine(c.CustomerID);
//也可以这样写
//var cust1 =((ObjectQuery<Customers>)cust).Execute(MergeOption.NoTracking);
//Esql写法
//string esql = "select value c from customersas c where c.CustomerID='ALFKI'";
//db.CreateQuery<Customers>(esql).Execute(MergeOption.NoTracking).FirstOrDefault();
}
}
l GetObjectByKey/First
GetObjectByKey:
在EF中,使用GetObjectByKey方法获取数据时,它首先会查询是否有缓存,如果有缓存则从缓存中返回需要的实体。如果没有则查询数据库,返回需要的实体,并添加在缓存中以便下次使用。
First: 总从数据库中提取需要的实体。
因此,我们应在合适的地方选择GetObjectByKey方法来获取数据,以减少对数据库的访问提升性能。示例代码如下:
[Test]
public voidGetByKeyTest()
{
using (var db =new NorthwindEntities1())
{
//从数据库中提取数据
var cst = db.Customers.First(c =>c.CustomerID == "ALFKI");
Console.WriteLine(cst.CustomerID);
//将从缓存中提取数据
EntityKey key = newEntityKey("NorthwindEntities1.Customers","CustomerID", "ALFKI");
var cst1 = db.GetObjectByKey(key) as Customers;
Console.WriteLine(cst1.CustomerID);
}
}
此外,需要注意的是如果GetObjectByKey没有获取到符合条件的数据,那么它会抛异常。为了避免此情况发生,在有可能出现异常的地方,我们应该使用TryGetObjectByKey方法。TryGetObjectByKey方法获取数据的方式和GetObjectByKey类似,只是当没有取到符合条件的数据时,TryGetObjectByKey会返回null而不是抛异常。示例代码如下:
[Test]
public voidTryGetByKeyTest()
{
using (var db =new NorthwindEntities1())
{
//没有符合条件的数据会有异常抛出
EntityKey key = newEntityKey("NorthwindEntities1.Customers","CustomerID", "♂风车车.Net");
var cst = db.GetObjectByKey(key) as Customers;
Console.WriteLine(cst.CustomerID);
//没有符合条件的数据会有返回null
EntityKey key1 = newEntityKey("NorthwindEntities1.Customers","CustomerID", "♂风车车.Net");
Object cst1 = null;
db.TryGetObjectByKey(key1, out cst1);
if (cst1 != null)
Console.WriteLine(((Customers)cst1).CustomerID);
}
}
l First /FirstOrDefault
First: 当我们使用First来获取数据,如果没有符合条件的数据,那么我们的代码将会抛出异常。
FirstOrDefault: 当我们使用FirstOrDefault来获取的数据,如果没有符合条件的数据,那么它将返回null。
显然,对于一个良好的代码,是对可以预见的异常进行处理,而不是等它自己抛出来。示例代码如下:
[Test]
public voidFirstTest()
{
using (var db =new NorthwindEntities1())
{
//抛异常的代码
var cst = db.Customers.First(c =>c.CustomerID == "♂风车车.Net");
Console.WriteLine(cst.CustomerID);//此处将出抛异常
//推荐的使用如下代码:
var cst1 = db.Customers.FirstOrDefault(c =>c.CustomerID == "♂风车车.Net");
if (cst1 != null)
Console.WriteLine(cst1.CustomerID);
}
}
l 延迟加载/Include
EF不支持实体的部分属性延迟加载,但它支持实体关系的延迟加载。默认情况,实体的关系是不会加载。如下代码:
[Test]
public voidIncludeTest()
{
using(var db = new NorthwindEntities1())
{
var csts = db.Customers;
foreach (var c in csts)
{
Console.WriteLine(c.CustomerID);
foreach (var o in c.Orders)
Console.WriteLine(" " + o.OrderID);
}
}
}
上述代码中,因为Orders没有被加载,所以在输出Orders的时候,是不会有任何输出的。
当我们需要加载某些关联的关系时,可是用Include方法,如下代码所示:
[Test]
public voidIncludeTest()
{
using (var db =new NorthwindEntities1())
{
var csts = db.Customers.Include("Orders");
foreach (var c in csts)
{
Console.WriteLine(c.CustomerID);
foreach (var o in c.Orders)
Console.WriteLine(" " + o.OrderID);
}
}
}
上述代码中,Customers关联的Orders将被加载。
Entity Framework 学习高级篇2—改善EF代码的方法(下)
本节,我们将介绍一些改善EF代码的方法,包括编译查询、存储模型视图以及冲突处理等内容。
l CompiledQuery
提供对查询的编译和缓存以供重新使用。当相同的查询需要执行很多遍的时候,那么我们可以使用ComplieQuery将查询的语句进行编译以便下次使用,这样可以免去对同一语句的多次处理,从而改善性能。
示例代码如下:
[Test]
public voidComplieTest()
{
using (var db =new NorthwindEntities1())
{
//对查询进行编译
var customer = CompiledQuery.Compile<NorthwindEntities1, IQueryable<Customers>>(
(database) => database.Customers.Where(c => c.City == "London"));
//执行20次相同的查询
for (int i = 0;i < 20; i++)
{
DateTime dt = System.DateTime.Now;
foreach (var c in customer(db))
Console.WriteLine(c.CustomerID);
Console.WriteLine(DateTime.Now.Subtract(dt).TotalMilliseconds);
Console.WriteLine("---------------------------------------------------");
}
}
}
l 存储模型视图
在EF中,当执行实体查询的时候,运行时首先将实体模型转换成ESQL视图,而ESQL视图则是根据msl文件来生成相应的代码。此外,ESQL视图包含了相应的查询语句。ESQL视图被创建后将在应用程序域中进行缓存以便下次使用。这个运行时生成存储模型视图是比较耗时的过程。
为了,免去运行时生成存储模型视图,我们可以预先产生这个的存储模型视图。具体步骤如下:
首先,使用EdmGen2来产生存储模型视图,相应的命令如下:
Edmgen2 /ViewGen cs NorthwindEntites.edmx
执行此命令后,edmgen2会在当前目录下生成一个名为NorthwindEntites.GeneratedViews.cs这个文件,就是我们要使用的存储模型视图文件。
将此文件添加到项目中就行,其他的代码不需要改变,EF会自动调用此视图文件。如下示例代码:
[Test]
public voidViewTest()
{
using (var db =new NorthwindEntities1())
{
var suppliers = db.Suppliers;
foreach (var s in suppliers)
Console.WriteLine(s.ContactName);
}
}
没有使用存储模型视图的情况是:
1 passed, 0 failed, 0 skipped, took 7.09seconds.
项目中添加了NorthwindEntites.GeneratedViews.cs文件,执行情况是:
1 passed, 0 failed, 0 skipped, took 5.38seconds.
可见,使用了存储模型视图的确是提高了性能。
l 冲突处理
在EF中,默认情况并不会检查并发冲突。因为EF实现的是乐观的并发模式,当有并发的冲突发生时,将会抛出Optimistic Concurrency Exception异常。我们可以通过使用RefreshMode这个枚举来指定当发生冲突时如何处理。
RefreshMode有两中枚举值:
ClientsWins: 当提交修改,更新数据库中的值。
StoreWins: 放弃修改,使用数据库中的值。
示例代码片段如下:
var db2 = new NorthwindEntities1();
var customer2 = db2.Customers.FirstOrDefault(c=> c.CustomerID == "2009");
if (customer2 != null)
{
customer2.ContactName = "♂风车车.Net";
customer2.City = "CD";
customer2.Region = "GX";
}
try
{
db2.SaveChanges();
}
catch (OptimisticConcurrencyExceptionex) //捕获到冲突,则进行相应的处理
{
db2.Refresh(RefreshMode.ClientWins,customer2);
db2.SaveChanges();
}
上述代码片段,只是说明怎么处理并发冲突,不是具体的并发。(ps:本来是准备开个线程来模拟并发的,但是始终没成功,没明白什么原因,望高人指点呢!)
前一段时间,一直在学习EF.对于这个EF多少算是有点了解吧. 目前,对于EF这个学习系列的文章,就暂时写到这里.当然还有一些相关的知识没有介绍,请各位朋友见谅!很乐意和大家交换关于EF学习的心得或技巧.后续,关于EF块东西,我还是会随时关注着,如果有什么好的心得或文章,我也会尽量拿出来给大家分享!每次看到一些朋友的相关留言讨论,我都无比的兴奋,有点小小成就感!
最后,感谢各位朋友的热情关注和支持!
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。