问题

你想为多次用到的查询提高性能,而且你不想添加额外的编码或配置.

解决方案

假设你有如Figure 13-8 所示的模型

Figure 13-8. A model with an Associate and its related Paycheck

在这个模型里,每个Associate(同事)有0到多个Paychecks(薪水),你有一个LINQ查询,它在你的整个应用程序中重复使用,你想仅编译一次,然后复用这个已编译的版本,通过这种方式来提高这个查询性能。

当针对数据库执行时,EF必须把你的强类型的LINQ查询转换成对应的SQL查询(基于你的数据库引擎,SqlServer,Oracle等等),在EF5时,每个查询转换在默认情况下会被缓存,这个过程与“自动缓存”相关,后面的每次LINQ查询,都直接从“查询计划缓存”里重新取回,这样就绕过了转换的步骤.对于包含参数的查询,改变参数值,仍然会重新获取相同的查询.有趣地是,这个”查询计划缓存”在同一个应用程序域里的上下文对象里共享,也就是说,一旦缓存了,在同一个应用程序域里的任何一个上下文对象都访问它.

在Listing 13-10,我们比较了启用和禁用缓存的性能.为说明带来的性能,我们把LinQ查询的编译版本和非编译版本迭代10次的时间打印出来.在这个查询里,我们可以看到大致有2倍的性能提升.多数情况下是由于编译需要相对高的成本,而执行查询却只需要低的成本.

Listing 13-20. Comparing the Performance of a Simple Compiled LINQ Query

private static void RunUncompiledQuery()

{

using (var context = new EFRecipesEntities())

{

// Explicitly disable query plan caching

var objectContext = ((IObjectContextAdapter)context).ObjectContext;

var associateNoCache = objectContext.CreateObjectSet<Associate>();

associateNoCache.EnablePlanCaching = false;

var watch = new Stopwatch();

long totalTicks = 0;

// warm things up

associateNoCache.Include(x => x.Paychecks).Where(a => a.Name.StartsWith("Karen")).ToList();

// query gets compiled each time

for (var i = 0; i < 10; i++)

{

watch.Restart();

associateNoCache.Include(x => x.Paychecks).Where(a => a.Name.StartsWith("Karen")).ToList();

watch.Stop();

totalTicks += watch.ElapsedTicks;

Console.WriteLine("Not Compiled #{0}: {1}", i, watch.ElapsedTicks);

}

Console.WriteLine("Average ticks without compiling: {0}", (totalTicks / 10));

Console.WriteLine("");

}

}

private static void RunCompiledQuery()

{

using (var context = new EFRecipesEntities())

{

var watch = new Stopwatch();

long totalTicks = 0;

// warm things up

context.Associates.Include(x => x.Paychecks).Where(a => a.Name.StartsWith("Karen")).ToList();

totalTicks = 0;

for (var i = 0; i < 10; i++)

{

watch.Restart();

context.Associates.Include(x => x.Paychecks).Where(a => a.Name.StartsWith("Karen")).ToList();

watch.Stop();

totalTicks += watch.ElapsedTicks;

Console.WriteLine("Compiled #{0}: {1}", i, watch.ElapsedTicks);

}

Console.WriteLine("Average ticks with compiling: {0}", (totalTicks / 10));

}

}

输出结果如下:

Not Compiled #0: 10014

Not Compiled #1: 5004

Not Compiled #2: 5178

Not Compiled #3: 7624

Not Compiled #4: 4839

Not Compiled #5: 5017

Not Compiled #6: 4864

Not Compiled #7: 5090

Not Compiled #8: 4499

Not Compiled #9: 6942

Average ticks without compiling: 5907

Compiled #0: 3458

Compiled #1: 1524

Compiled #2: 1320

Compiled #3: 1283

Compiled #4: 1202

Compiled #5: 1145

Compiled #6: 1075

Compiled #7: 1104

Compiled #8: 1081

Compiled #9: 1084

Average ticks with compiling: 1427

它是如何工作的

当你运行一个LINQ查询时,EF为该查询创建一个表达式树对象,然后该对象转换或编译入一个内部命令树.该内部命令树会被传递给数据库提供者并被转换为相应的数据库命令(通常是SQL).转换一个表达式树的代价可能相当高,主要取决于查询复杂度和底层的模型.模型如果有很深层的继承或是很多的水平方向上的引入,会使得转换处理过程相当复杂,这样编译的所花的时间要比执行查询所花的时间多得多.然后在EF5为LINQ查询引入了查询自动缓存技术.你可以通过查看Listing 13-20 的执行结果里看出它所提高的性能.

另外,如Listing 13-20 所示,你也能禁用”自动编译”特性,通过DbContext对象的底层对象ObjectContext,得到一个实体对象的引用,并设置它的EnablePlanCaching属性为false.

为了跟踪每个已编译的查询,EF遍历查询表达式树节点,并创建一个哈希表,用它作为已编译查询的索引,为后面的每个调用,EF会先尝试从缓存查找哈希表的主键,以节省查询转换处理带来的成本.需要注意的是,”查询缓存计划”不依赖上下文对象,它是被绑定到应用程序的应用程序域,也就意味着,缓存的查询对于所有的上下文实例都是可用的.

当底层的查询缓存包含800或更多缓存计划时,每一分钟,一个清除处理会根据LFRU(least frequently/recently used使用次数最少,最近不用)算法(根据查询被命中的次数和它的时限)来移除一个缓存.

已编译的查询对Asp.net的分页查询尤其有用,分页查询的参数可能会改变,但是查询是一致的,也是能复用到每一页的展示上,这是因为一个已编译的查询是”被参数化的”,也就是说能接受不同的参数值.

Entity Framework 6 Recipes 2nd Edition(13-6)译 -> 自动编译的LINQ查询的更多相关文章

  1. Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新

    因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...

  2. Entity Framework 6 Recipes 2nd Edition(9-1)译->用Web Api更新单独分离的实体

    第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...

  3. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  4. Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪

    9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...

  5. Entity Framework 6 Recipes 2nd Edition(11-11)译 -> 在LINQ中调用数据库函数

    11-11. 在LINQ中调用数据库函数 问题 相要在一个LINQ 查询中调用数据库函数. 解决方案 假设有一个任命(Appointment )实体模型,如Figure 11-11.所示, 我们想要查 ...

  6. Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体

    问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...

  7. Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体

    问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ...

  8. Entity Framework 6 Recipes 2nd Edition(13-4)译 -> 有效地创建一个搜索查询

    问题 你想用LINQ写一个搜索查询,能被转换成更有效率的SQL.另外,你想用EF的CodeFirst方式实现. 解决方案 假设你有如下Figure 13-6所示的模型 Figure 13-6. A s ...

  9. Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高

    问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...

  10. Entity Framework 6 Recipes 2nd Edition(13-9)译 -> 避免Include

    问题 你想不用Include()方法,立即加载一下相关的集合,并想通过EF的CodeFirst方式实现. 解决方案 假设你有一个如Figure 13-14所示的模型: Figure 13-14. A ...

随机推荐

  1. Fis3的前端模块化之路[基础篇]

    Fis3版本:v3.4.22 fis3是一个构建工具 解决前端开发中自动化工具.性能优化.模块化框架.开发规范.代码部署.开发流程等问题. 安装 npm install -g fis3 运行 fis3 ...

  2. [linux]阿里云主机的免登陆安全SSH配置与思考

    公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...

  3. PHP以接口方式实现多重继承(完全模拟)--学习笔记

     1.UML类图: 2.PHP代码: <?php /** * Created by PhpStorm. * User: andy * Date: 16-11-23 * Time: 下午7:57 ...

  4. 自己实现一个javascript事件模块

    nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...

  5. Angular源码分析之$compile

    @(Angular) $compile,在Angular中即"编译"服务,它涉及到Angular应用的"编译"和"链接"两个阶段,根据从DO ...

  6. so 问题来了,你现在值多少钱?

    年底了一大帮人都写着年底总结,总结一年做过的事.错过的事和做错的事.增长了多少本事,找没找到女朋友……来年做好升职加薪,要么做跳槽的准备,程序猿又开始浮躁了……. so 问题来了,你现在值多少钱? 这 ...

  7. jquery.cookie的使用

    今天想到了要为自己的影像日记增加赞的功能,并且需要用到cookie. 记得原生的js操作cookie也不是很麻烦的,但似乎jquery更简单,不过相比原生js,需要额外引入2个文件,似乎又不是很好,但 ...

  8. js面向对象学习 - 对象概念及创建对象

    原文地址:js面向对象学习笔记 一.对象概念 对象是什么?对象是“无序属性的集合,其属性可以包括基本值,对象或者函数”.也就是一组名值对的无序集合. 对象的特性(不可直接访问),也就是属性包含两种,数 ...

  9. 自己实现简单Spring Ioc

    IoC则是一种 软件设计模式,简单来说Spring通过工厂+反射来实现IoC. 原理简单说明: 其实就是通过解析xml文件,通过反射创建出我们所需要的bean,再将这些bean挨个放到集合中,然后对外 ...

  10. C语言可以开发哪些项目?

    C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多初学者在学习的过程中难免会出现迷茫,比如:不知道C语言可以开发哪些项目,可以应用在哪些实际的开发中--,这些迷茫也导致了我们在学习的过程 ...