学习entity framework期间收集的文章,转自http://www.cnblogs.com/hiteddy/archive/2011/10/01/Difference_among_IQueryable_IEnumeralb_IList_in_Entity_Framework.html

使用Entity Framework等ORM框架的时候,SQL对于使用者来说是透明的,往往很多人也不关心ORM所生成的SQL,然而系统出现性能问题的时候就必须关注生成的SQL以发现问题所在。

使用过Toplink的朋友知道很只要设置日志打印级别=FINE就可以配置使之生成的SQL在服务器中打印出来,Entiry Framework没有那么幸运,在以前要检测生成SQL的唯一方法是SQL Server Profiler,但使用起来并不方便,结果也不能自动保存到文件中。

Tracing and Caching Provider Wrappers for Entity Framework是Entity Framework Team新推出的开源SQL追踪和二级缓存的解决方案。原理是在负责执行具体SQL语句的data provider(SqlClient或者其他Client)之上插入了一层WrappingProvider,用于监控DbCommand.ExecuteReader(), ExecuteScalar() and ExecuteNonQuery(),将Sql命令输出到指定介质或者将查询结果缓存起来以重用。

使用方法很简单,下载源代码编译后将dll添加到项目中,新加一个类WrappedNorthWindEntities继承原有的Entities即可,详见源代码中的示例。

测试IQueryable, IEnumerable, IList的区别

下面我们使用EF Wrapper来监测Entify Framework中IQueryable, IEnumerable和IList所生成的SQL。

TestIQueryable
private static void TestIQueryable()
{
using (var ctx = new WrappedNorthWindEntities())
{
IQueryable<Product> expression = ctx.Products.Take(5);
IQueryable<Product> products = expression.Take(2); // A 不执行SQL
Console.WriteLine(products.Count());          // B SELECT COUNT(1) FROM ( SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] ))
Console.WriteLine(products.Count());          // C SELECT COUNT(1) FROM ( SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] ))
foreach (Product p in products)             // D SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products]
{
Console.WriteLine(p.ProductName);
}
foreach (Product p in products)             // E SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] )
{
Console.WriteLine(p.ProductName);
}
}
}

TestIEnumerable

private static void TestIEnumerable()
{
using (var ctx = new WrappedNorthWindEntities())
{
IEnumerable<Product> expression = ctx.Products.Take(5).AsEnumerable();
IEnumerable<Product> products = expression.Take(2); // A 不执行SQL
Console.WriteLine(products.Count());          // B SELECT TOP (5) * FROM [dbo].[Products]
Console.WriteLine(products.Count());          // C SELECT TOP (5) * FROM [dbo].[Products]
foreach (Product p in products)             // D SELECT TOP (5) * FROM [dbo].[Products]
{
Console.WriteLine(p.ProductName);
}
foreach (Product p in products)             // E SELECT TOP (5) * FROM [dbo].[Products]
{
Console.WriteLine(p.ProductName);
}
}
}

private static void TestIEnumerable()
{
using (var ctx = new WrappedNorthWindEntities())
{
IEnumerable<Product> expression = ctx.Products.Take(5).AsEnumerable();
IEnumerable<Product> products = expression.Take(2); // A 不执行SQL
Console.WriteLine(products.Count());          // B SELECT TOP (5) * FROM [dbo].[Products]
Console.WriteLine(products.Count());          // C SELECT TOP (5) * FROM [dbo].[Products]
foreach (Product p in products)             // D SELECT TOP (5) * FROM [dbo].[Products]
{
Console.WriteLine(p.ProductName);
}
foreach (Product p in products)             // E SELECT TOP (5) * FROM [dbo].[Products]
{
Console.WriteLine(p.ProductName);
}
}
}

TestIList

private static void TestIList()
{
using (var ctx = new WrappedNorthWindEntities())
{
var expression = ctx.Products.Take(5);
IList<Product> products = expression.Take(2).ToList(); // A SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products]

Console.WriteLine(products.Count());           // B 不执行SQL
Console.WriteLine(products.Count());           // C 不执行SQL
foreach (Product p in products)              // D 不执行SQL
{
Console.WriteLine(p.ProductName);
}
foreach (Product p in products)             // E 不执行SQL
{
Console.WriteLine(p.ProductName);
}
}
}

private static void TestIList()
{
using (var ctx = new WrappedNorthWindEntities())
{
var expression = ctx.Products.Take(5);
IList<Product> products = expression.Take(2).ToList(); // A SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] Console.WriteLine(products.Count());           // B 不执行SQL
Console.WriteLine(products.Count());           // C 不执行SQL
foreach (Product p in products)              // D 不执行SQL
{
Console.WriteLine(p.ProductName);
}
foreach (Product p in products)             // E 不执行SQL
{
Console.WriteLine(p.ProductName);
}
}
}

测试结果

  1. IQueryable和IEnumerable都是延时执行(Deferred Execution)的,而IList是即时执行(Eager Execution)
  2. IQueryable和IEnumerable在每次执行时都必须连接数据库读取,而IList读取一次后,以后各次都不需连接数据库。前两者很容易造成重复读取,性能低下,并且可能引发数据不一致性
  3. IQueryable和IEnumerable的区别:IEnumberalb使用的是LINQ to Object方式,它会将AsEnumerable()时对应的所有记录都先加载到内存,然后在此基础上再执行后来的Query。所以上述TestIEnumerable例子中执行的SQL是"select top(5) ...",然后在内存中选择前两条记录返回。

以下是一个IQueryable引发数据不一致性的例子:记录总数和记录详情两者本应一致,但由于IQueryable前后两次读取数据库,结果是现实有10条记录,却输出11条详情。

IQueryable Data Inconsistancy

IQueryable<Product> products = ctx.Products.All();
//开始的时候数据库product表中有10条记录, count = 10
int count = products.Count();
Console.WriteLine("Count of products:"+count);

//此时另一进程添加一个产品进数据库
//会重新读取数据库并输出11个产品名称
foreach (Product p in products)
{
Console.WriteLine(p.ProductName);
}

    IQueryable<Product> products = ctx.Products.All();
//开始的时候数据库product表中有10条记录, count = 10
int count = products.Count();
Console.WriteLine("Count of products:"+count);
    //此时另一进程添加一个产品进数据库
    //会重新读取数据库并输出11个产品名称
foreach (Product p in products)
{
Console.WriteLine(p.ProductName);
}

结论

基于性能和数据一致性这两点,我们使用IQueryable时必须谨慎,而在大多数情况下我们应使用IList。

  • 当你打算马上使用查询后的结果(比如循环作逻辑处理或者填充到一个table/grid中),并且你不介意该查询会即时执行,使用ToList()
  • 当你希望查询后的结果可以供调用者(Consummer)作后续查询(比如这是一个"GetAll"的方法),或者你希望该查询延时执行,使用AsQueryable()

使用工具追踪Entity Framework生成的SQL的更多相关文章

  1. 监听Entity Framework生成的Sql语句

               Entity Framework为我们提供了很大的方便,但有时候,我们想看看EF生成的Sql语句到底是怎样的,一种方式是我们可以启用Sql Server Profer工具.今天介 ...

  2. ahjesus 捕获entity framework生成的sql语句

    网上这方面的资料很少,找到一个可以用的 http://code.msdn.microsoft.com/EFProviderWrappers 里面有dll可以下载,有教程,不过是E文的. 在Entity ...

  3. Entity framework 生成的SQL如何设置兼容低版本的数据(转载)

    来源:https://q.cnblogs.com/q/84401/ 右键 edmx 文件,有xml方式打开. 将ProviderManifestToken 改为 2008 .

  4. Entity Framework Code First+SQL Server,改变聚集索引,提高查询性能

    .net Entity Framework(调研的是Entity Framework 4.0) code first方式生成数据库时,不能修改数据库表的索引,而SQLServer默认会把数据表的主键设 ...

  5. Entity Framework Core 执行SQL语句和存储过程

    无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...

  6. MiniProfiler工具介绍(监控EF生成的SQL语句)--EF,迷你监控器,哈哈哈

    十年河东,十年河西,莫欺少年穷... 今天是抄袭的别人的博客,不过我感觉蛮好,挺有用,特别是老板让你优化EF项目SQL耗时的情况下,你可以采用这种方式来优化你的LINQ. 时间很宝贵,废话还是不多说, ...

  7. Entity Framework执行原生SQL语句

    ExecuteSqlCommand为执行命令的接口, SqlQuery 为返回查询结果 1.Database.ExecuteSqlCommand 方法 (String, Object[]) 对数据库执 ...

  8. Entity Framework 中使用SQL Server全文索引(Full Text Search)

    GitHub:https://github.com/fissoft/Fissoft.EntityFramework.Fts EntityFramework中原来使用全文索引有些麻烦,需要使用DbCon ...

  9. Entity Framework中执行Sql语句

           如果想在EF框架中执行Sql语句,其实很简单,EF里面已经提供了相关的方法(此处使用的EF为EF4.1版本).        EF中提供了两个方法,一个是执行查询的Sql语句SqlQue ...

随机推荐

  1. 以http形式启动uwsgi服务

    uwsgi yourfile.ini # 配置文件 [uwsgi] http = 127.0.0.1:3106 socket = 127.0.0.1:3006 chdir = /www/student ...

  2. linux回到上次目录与历史命令查找快捷方式

    # cd -进入上次访问目录 二.历史命令搜索操作快捷键:[Ctrl + r], [Ctrl + p], [Ctrl + n] 在终端中按捉 [Ctrl] 键的同时 [r] 键,出现提示:(rever ...

  3. PHP MongoDB 扩展安装配置

    近日对MongoDB比较感兴趣,在linux下部署了一套LAMP,想把MongoDB加进来,下面进入正题: 1.确保安装好LAMP环境,假设php安装目录:/usr/local/php5 2.下载ht ...

  4. 一个字符串中可能包含a~z中的多个字符,如有重复,如String data="aavzcadfdsfsdhshgWasdfasdf",求出现次数最多的那个字母及次数,如有多个重复的则都求出。

    主要掌握String中的方法 char[] toCharArray()           将此字符串转换为一个新的字符数组. int indexOf(String str)           返回 ...

  5. Bootstrap 巨幕页头缩略图和警告框组件

    一.巨幕组件 //在固定的范围内,有圆角 <div class="container"> <div class="jumbotron"> ...

  6. Qt配置信息设置(QSettings在不同平台下的使用路径)

    在Windows操作系统中,大多把配置文件信息写在注册表当中,或写在*.ini文件中,对于这两种操作都有相应的Windows API函数,在以前的文章中都提及过,这里就不多说了~ 在Qt中,提供了一个 ...

  7. Asp.net MVC 版本简史

    http://www.dotnet-tricks.com/Tutorial/mvc/XWX7210713-A-brief-history-of-Asp.Net-MVC-framework.html A ...

  8. Wordpress制作sidebar.php

    调用 在主页以下方法可以调用模板中sidebar.php的内容 <?php get_sidebar(); ?> 判断是否自定义sidebar侧边栏: <?php if ( !func ...

  9. EntityFramework更新数据

    1.TryUpdateModel 使用很方便,但实际更新数据的过程还是先select,再update.另外发现一个问题,对于input的type类型file的字段,无法使用TryUpdateModel ...

  10. 解决libc.so.6: version `GLIBC_2.14' not found问题

    今天centos新机器上运行项目的时候出现题目所示的错误,搜索后发现是底层glibc 版本太低导致. strings /lib64/libc.so.6 |grep GLIBC_ 使用上面的命令发现 g ...