EntityFramework和EntityFramework.Extended使用说明——性能,语法和产生的sql

环境说明:EntityFramework 6.1.3和.Net Framework4.5
性能注意事项:https://msdn.microsoft.com/zh-cn/library/cc853327.aspx
比较精髓的一点:查询执行的各个阶段中的准备查询,每个唯一查询一次。包括编写查询命令、基于模型和映射元数据生成命令树和定义所返回数据的形状的成本。 因为实体 SQL查询命令和 LINQ 查询现已缓存,所以,以后执行相同查询所需的时间较少。
如果有缓存的话,那么查询命令转成sql语句的性能会进一步提高,是不是ORM的效率更接近Ado.Net了呢?

性能注意点:此处参考了 http://www.cnblogs.com/jake1/archive/2013/04/25/3043664.html
a.在数据库里面分页
b.延迟加载要合理使用
c.需要连表的地方要连表查询
d.查询数据库的次数和发出的sql语句的数量和长度
e.NoTracking的使用

表ContactInfo,GroupInfo说明:

CREATE TABLE [dbo].[ContactInfo](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [ContactId] [nvarchar](128) NOT NULL,
    [IsDelete] [int] NOT NULL,
    [Account] [nvarchar](64) NOT NULL,
    [ContactName] [nvarchar](50) NOT NULL,
    [CommonMobile] [nvarchar](50) NULL,
    [HeadPortrait] [nvarchar](256) NULL,
    [AttFile] [nvarchar](256) NULL,
    [GroupId] [int] NULL,
 CONSTRAINT [PK_ContactInfo] PRIMARY KEY CLUSTERED
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[GroupInfo](
    [GroupId] [int] IDENTITY(1,1) NOT NULL,
    [GroupName] [nvarchar](300) NOT NULL,
 CONSTRAINT [PK_GroupInfo] PRIMARY KEY CLUSTERED
(
    [GroupId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

表的说明

1.0 分页查询

c#语句:

var db = new PhoneBookEntities();
db.GroupInfo.Where(c => c.GroupName.Length>=2).OrderByDescending(c => c.GroupId).Skip(2).Take(3).ToArray();

sql语句:

SELECT TOP (3)
[Filter1].[GroupId] AS [GroupId],
[Filter1].[GroupName] AS [GroupName]
FROM
( SELECT [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName],
row_number() OVER (ORDER BY [Extent1].[GroupId] DESC) AS [row_number]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE (LEN([Extent1].[GroupName])) >= 2
) AS [Filter1]

WHERE [Filter1].[row_number] > 2
ORDER BY [Filter1].[GroupId] DESC

2.0 FirstOrDefault,First

c#语句:

var db = new PhoneBookEntities();
db.GroupInfo.FirstOrDefault(c => c.GroupId == 1);

sql语句:

SELECT TOP (1)
[Extent1].[GroupId] AS [GroupId],
[Extent1].[GroupName] AS [GroupName]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE 1 = [Extent1].[GroupId]

延迟加载:

var db = new PhoneBookEntities();
var ci = db.ContactInfo.FirstOrDefault(c => c.ID == 9);

/* 此时产生的sql:*/

SELECT TOP (1)
[Extent1].[ID] AS [ID],
[Extent1].[ContactId] AS [ContactId],
[Extent1].[IsDelete] AS [IsDelete],
[Extent1].[Account] AS [Account],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[CommonMobile] AS [CommonMobile],
[Extent1].[HeadPortrait] AS [HeadPortrait],
[Extent1].[AttFile] AS [AttFile],
[Extent1].[GroupId] AS [GroupId]
FROM [dbo].[ContactInfo] AS [Extent1]
WHERE 9 = [Extent1].[ID] 
var gn= ci.GroupInfo.GroupName;

/* (运行到此行c#代码才会)产生sql:*/

exec sp_executesql N‘SELECT
[Extent1].[GroupId] AS [GroupId],
[Extent1].[GroupName] AS [GroupName]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE [Extent1].[GroupId] = @EntityKeyValue1‘,N‘@EntityKeyValue1 int‘,@EntityKeyValue1=1

如果是一条数据,用延迟加载是OK的.如果上面查询有多条如10条结果,每条结果都使用到GroupInfo属性,那么一共会有11条sql请求.效率低.应该使用连表,一条sql搞定.写法如下.

第一种写法 Join:

那如果是多条数据,应使用预加载.
c#语句:

var db = new PhoneBookEntities();
var ci = db.ContactInfo.Where(c => c.ID >3).Join(db.GroupInfo,c=>c.GroupId,g=>g.GroupId,(c,g)=>new{c.ContactName,g.GroupName});
foreach (var item in ci)
{
    MessageBox.Show(item.ContactName + "->" + item.GroupName);
}

sql语句:

SELECT
[Extent1].[ID] AS [ID],
[Extent1].[ContactName] AS [ContactName],
[Extent2].[GroupName] AS [GroupName]
FROM [dbo].[ContactInfo] AS [Extent1]
INNER JOIN [dbo].[GroupInfo] AS [Extent2] ON [Extent1].[GroupId] = [Extent2].[GroupId]
WHERE [Extent1].[ID] > 3

说明:GroupJoin的用法,和Join使用类似.区别在于第四个参数resultSelector.

Join的第四个参数是 Func<ContactInfo,GroupInfo,anonymous type>.
GroupJoin的第四个参数是 Func<ContactInfo,IEnumerable<GroupInfo>,anonymous type>.
使用场景:联系人和他的好友.联系人一张表,好友关系一张表.联系人表和好友关系表做连接,查出多个联系人数据(包含他的好友),就应该使用GroupJoin.
简单说:
public partial class ContactInfo
{
public int ID { get; set; }
public string ContactName { get; set; }
public Nullable<int> GroupId { get; set; }
public virtual GroupInfo GroupInfo { get; set; }
}
ContactInfo和GroupInfo一对一,该用Join;
如果是这种情况(仅仅是假设)
public partial class ContactInfo
{
public int ID { get; set; }
public string ContactName { get; set; }
public Nullable<int> GroupId { get; set; }
public virtual List<GroupInfo> GroupInfo { get; set; }
}
ContactInfo和GroupInfo一对多,该用GroupJoin;

第二种写法 Include:

注意:数据库设计ContactInfo,GroupInfo 要有主外键关系.
c#语句:

var db = new PhoneBookEntities();
var ci = db.ContactInfo.Include("GroupInfo").Where(c => c.ID > 3).Select(c => new { c.ContactName, c.GroupInfo.GroupName });
foreach (var item in ci)
{
    MessageBox.Show(item.ContactName + "->" + item.GroupName);
}

sql语句:

SELECT
[Extent1].[ID] AS [ID],
[Extent1].[ContactName] AS [ContactName],
[Extent2].[GroupName] AS [GroupName]
FROM [dbo].[ContactInfo] AS [Extent1]
LEFT OUTER JOIN [dbo].[GroupInfo] AS [Extent2] ON [Extent1].[GroupId] = [Extent2].[GroupId]
WHERE [Extent1].[ID] > 3

3.0 Add

c#语句:

var db = new PhoneBookEntities();
var giModel = new GroupInfo();
giModel.GroupName = "Test";
db.GroupInfo.Add(giModel);
db.SaveChanges();
//Add方法之后,会把数据库表记录中的GroupId给giModel.

sql语句:

exec sp_executesql N‘INSERT [dbo].[GroupInfo]([GroupName])
VALUES (@0)
SELECT [GroupId]
FROM [dbo].[GroupInfo]
WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()‘,N‘@0 nvarchar(300)‘,@0=N‘Test‘

4.0 AddRange

c#语句:

var db = new PhoneBookEntities();
var gi = new GroupInfo[] { new GroupInfo() { GroupName = "g1" }, new GroupInfo() { GroupName = "g2" }, new GroupInfo() { GroupName = "g3" }, };
db.GroupInfo.AddRange(gi);
db.SaveChanges();

sql语句:

exec sp_executesql N‘INSERT [dbo].[GroupInfo]([GroupName])
VALUES (@0)
SELECT [GroupId]
FROM [dbo].[GroupInfo]
WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()‘,N‘@0 nvarchar(300)‘,@0=N‘g1‘

exec sp_executesql N‘INSERT [dbo].[GroupInfo]([GroupName])
VALUES (@0)
SELECT [GroupId]
FROM [dbo].[GroupInfo]
WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()‘,N‘@0 nvarchar(300)‘,@0=N‘g2‘

...

共执行3次.但是连接只打开关闭了1次.

5.0 Remove

c#语句:

var db = new PhoneBookEntities();
var ci= db.GroupInfo.FirstOrDefault(c => c.GroupId == 214);
db.GroupInfo.Remove(ci);
db.SaveChanges();

sql语句:

SELECT TOP (1)
[Extent1].[GroupId] AS [GroupId],
[Extent1].[GroupName] AS [GroupName]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE 214 = [Extent1].[GroupId]

exec sp_executesql N‘DELETE [dbo].[GroupInfo]
WHERE ([GroupId] = @0)‘,N‘@0 int‘,@0=214

感觉繁琐啦?解决方案,1,自己定义方法;2,引用EntityFramework.Extended

来源https://www.nuget.org/packages/EntityFramework.Extended/或者在当前项目里,引用点击右键,选择管理NuGet程序包,联机搜索并下载.
引用之后操作就简单了.

c#语句:

var db = new PhoneBookEntities();
db.GroupInfo.Delete(c => c.GroupName == "Test");//过时的方法
db.GroupInfo.Where(c => c.GroupName == "Test").Delete();//如果查到的记录数为0,也不报错.上边的先查 FirstOrDefault 后删 Remove,你猜猜报错吗
db.SaveChanges();

sql语句:

DELETE [dbo].[GroupInfo]
FROM [dbo].[GroupInfo] AS j0 INNER JOIN (
SELECT
[Extent1].[GroupId] AS [GroupId]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE N‘Test‘ = [Extent1].[GroupName]
) AS j1 ON (j0.[GroupId] = j1.[GroupId])

一条sql语句搞定删除.

6.0 更新操作

基于EntityFramework.Extended的更新操作.
c#语句:

db.GroupInfo.Where(c => c.GroupName.Contains("g")).Update(c => new GroupInfo() { GroupName = c.GroupName+"!"});
//此处没有db.SaveChanges();,一样执行了操作.

sql语句:

UPDATE [dbo].[GroupInfo] SET
[GroupName] = [GroupName] + N‘!‘
FROM [dbo].[GroupInfo] AS j0 INNER JOIN (
SELECT
[Extent1].[GroupId] AS [GroupId]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE [Extent1].[GroupName] LIKE N‘%g%‘
) AS j1 ON (j0.[GroupId] = j1.[GroupId])

也是一条sql语句搞定批量修改.

(注意:update语句中只set了GroupName字段;
不用EntityFramework.Extended,用EF的先查后改,sql语句也是只set了GroupName字段.
更特殊的情况,以下例子:

var gi = db.GroupInfo.FirstOrDefault(c => c.GroupId == 219 );//此条记录的GroupName为"Test"
gi.GroupName = "Test";
db.SaveChanges();

EF会自动优化,最终结果只有一个select语句,而没有update语句. 此处细节,赞!

对比NHibernate 4,以下两行代码产生的sql语句会set Product表的[所有]字段
var pl = session.Query<Product>().FirstOrDefault(c => c.Name == "cnblogs");
pl.Name = "ICE";
)
如果先查出来要更改的数据,再修改.也是可以的. 但是从效率考虑,不管是c#写法还是产生的sql语句,基于EntityFramework.Extended的更新操作更优.

7.0 EntityFramework.Extended中Future的使用

c#语句:

var db = new PhoneBookEntities();
var fci = db.ContactInfo.Where(c => c.ID > 1).FutureFirstOrDefault();
var fgi = db.GroupInfo.Where(c => c.GroupId > 2).FutureFirstOrDefault();
ContactInfo ci= fci.Value;
GroupInfo gi = fgi.Value;

//采用Future的写法,不会立即查询数据库.只要调用结果的任意一个 .ToList,.ToArray或者.Value ,才会查数据库.并且只发一个请求(Query #1 + Query #2 拼接好后发给数据库,一起执行语句).

sql语句:

-- Query #1

SELECT TOP (1)
[Extent1].[ID] AS [ID],
[Extent1].[ContactId] AS [ContactId],
[Extent1].[IsDelete] AS [IsDelete],
[Extent1].[Account] AS [Account],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[CommonMobile] AS [CommonMobile],
[Extent1].[HeadPortrait] AS [HeadPortrait],
[Extent1].[AttFile] AS [AttFile],
[Extent1].[GroupId] AS [GroupId]
FROM [dbo].[ContactInfo] AS [Extent1]
WHERE [Extent1].[ID] > 1;

-- Query #2

SELECT TOP (1)
[Extent1].[GroupId] AS [GroupId],
[Extent1].[GroupName] AS [GroupName]
FROM [dbo].[GroupInfo] AS [Extent1]
WHERE [Extent1].[GroupId] > 2;

标注:EntityFramework.Extended相关部分参考了 顾振印的博文: http://www.cnblogs.com/GuZhenYin/p/5482288.html

8.0 AsNoTracking

c#语句:

var db = new PhoneBookEntities();
var gi = db.GroupInfo.FirstOrDefault(c => c.GroupId == 219);
MessageBox.Show(db.Entry(gi).State.ToString());//Unchanged
var giAnk = db.GroupInfo.AsNoTracking().FirstOrDefault(c => c.GroupId == 219);
MessageBox.Show(db.Entry(giAnk).State.ToString());//Detached
时间: 2025-01-20 11:43:30

EntityFramework和EntityFramework.Extended使用说明——性能,语法和产生的sql的相关文章

第二十三节: EF性能篇(三)之基于开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题

一. 开篇说明 EF的性能问题一直以来经常被人所吐槽,究其原因在于“复杂的操作在生成SQL阶段耗时长,且执行效率不高”,但并不是没有办法解决,从EF本身举几个简单的优化例子: ①:如果仅是查询数据,并不对数据进行增.删.改操作,查询数据的时候可以取消状态追踪. db.TestInfor.AsNoTracking().FirstOrDefault(); ②:用什么查什么,比如一张表有100多个字段,本次业务只需要5个字段,一定是select这5个字段,然后toList,而不是全部查询,再toLis

PLSQL_性能优化效能跟踪工具SQL Trace分析(案例)

2014-06-25 BaoXinjian 一.摘要 SQL TRACE是Oracle提供的用于进行SQL跟踪的手段,是强有力的辅助诊断工具.在日常的数据库问题诊断和解决中,SQL TRACE是非常常用的方法.一般,一次跟踪可以分为以下几步:1.界定需要跟踪的目标范围,并使用适当的命令启用所需跟踪.2.经过一段时间后,停止跟踪.此时应该产生了一个跟踪结果文件.3.找到跟踪文件,并对其进行格式化,然后阅读或分析. 另文已介绍了其他的跟踪工具DBMS_PROFILER, Form Trace, Re

Oracle性能分析1:开启SQL跟踪和获取trace文件

当Oracle查询出现效率问题时,我们往往需要了解问题所在,这样才能针对问题给出解决方案.Oracle提供了SQL执行的trace信息,其中包含了SQL语句的文本信息,一些执行统计,处理过程中的等待,以及解析阶段(如生成执行计划)产生的信息.这些信息有助于你分解sql语句的服务时间和等待时间,并了解所用资源和同步点的详细信息,从而帮助你诊断存在的性能问题. 这篇文章介绍了怎么开启SQL跟踪和获取trace文件,详细信息如下. 开启SQL跟踪 从内部技术细节看,SQL跟踪是基于10046调试事件的

EntityFramework之原始查询及性能提升(六)

前言 在EF中我们可以通过Linq来操作实体类,但是有些时候我们必须通过原始sql语句或者存储过程来进行查询数据库,所以我们可以通过EF Code First来实现,但是SQL语句和存储过程无法进行映射,于是我们只能手动通过上下文中的SqlQuery和ExecuteSqlCommand来完成. SqlQuery sql语句查询实体 通过DbSet中的SqlQuery方法来写原始sql语句返回实体实例,如果是通过Linq查询返回的那么返回的对象将被上下文(context)所跟踪. 首先给出要操作的

EntityFramework之原始查询及性能优化(六)

前言 在EF中我们可以通过Linq来操作实体类,但是有些时候我们必须通过原始sql语句或者存储过程来进行查询数据库,所以我们可以通过EF Code First来实现,但是SQL语句和存储过程无法进行映射,于是我们只能手动通过上下文中的SqlQuery和ExecuteSqlCommand来完成. SqlQuery sql语句查询实体 通过DbSet中的SqlQuery方法来写原始sql语句返回实体实例,如果是通过Linq查询返回的那么返回的对象将被上下文(context)所跟踪. 首先给出要操作的

【性能诊断】二、单功能场景的性能分析(fiddler、SQL Profiler)

Fiddler fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说,在诊断分析问题时,都有很大的帮助. 下载地址:http://www.telerik.com/download/fiddler 工作原理和使用说明可参考:http://www.cnblogs.com/TankXiao/archive/2012/02/06/2337728.html 当然我们如果

Oracle 学习之性能优化(一)SQL语句处理

当向Oracle提交一个sql命令时,Oracle到底做了哪些事情?对这个问题有很好的理解,能帮助你更好的分析sql语句的优化. 执行一条sql语句从开始到结束,需要经历4个步骤: 分析--对提交的语句进行语法分析.语义分析和共享池检查. 优化--生成一个可在数据库中用来执行语句的最佳计划 行资源生成--为会话取得最佳计划并建立执行计划 语句执行--完成实际执行查询的行资源生成步骤的输出.对应DDL来说,这一步就是语句的结   束.对应SELECT来说,这一步是取数据的开始. 以上步骤,有的是可

SQL Server SQL性能优化之--通过拆分SQL提高执行效率,以及性能高低背后的原因

复杂SQL拆分优化 拆分SQL是性能优化一种非常有效的方法之一, 具体就是将复杂的SQL按照一定的逻辑逐步分解成简单的SQL,借助临时表,最后执行一个等价的逻辑,已达到高效执行的目的 一直想写一遍通过拆分SQL来优化的博文,最近刚好遇到一个实际案例,比较有代表性,现分享出来, 我们来通过一个案例来分析,为什么拆分语句可以提高SQL执行效率,更重要的是弄清楚,拆分前为什么慢,拆分后为什么快了? 幼稚的话,各位看官莫笑 先看一下相关表的数据量,大表也有5900多万,小表有160多万 (声明:我从来没

小蚂蚁学习mysql性能优化(1)--SQL以及索引优化

性能优化之mysql优化 可以从几个方面进行优化 硬件    系统配置    数据库表结构    SQL索引 成本从高到底,效果从低到高. 如何发现有问题的SQL? 使用mysql慢查询日志对有效率问题的sql进行监控. show variables like 'slow_query.log'; set global slow_query_log_file='/home/mysql/sql_log/mysql-slow.log';//日志存放的位置 set global log_queries_