问题

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

解决方案

假设你有如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. 关于解决python线上问题的几种有效技术

    工作后好久没上博客园了,虽然不是很忙,但也没学生时代闲了.今天上博客园,发现好多的文章都是年终总结,想想是不是自己也应该总结下,不过现在还没想好,等想好了再写吧.今天写写自己在工作后用到的技术干货,争 ...

  2. .NET Core全新路线图

    .NET Core / ASP.NET Core 1 RTM发布两周后,社区也很积极,收到了非常多的反馈,上周五微软的scott Hunter 在dotnet团队官方博客上发布了.NET Core全新 ...

  3. 来,给Entity Framework热热身

    先来看一下Entity Framework缓慢的初始化速度给我们更新程序带来的一种痛苦. 我们手动更新程序时通常的操作步骤如下: 1)把Web服务器从负载均衡中摘下来 2)更新程序 3)预热(发出一个 ...

  4. 运行执行sql文件脚本的例子

    sqlcmd -s -d db_test -r -i G:\test.sql 黑色字体为关键命令,其他颜色(从左至右):服务器名称,用户名,密码,数据库,文件路径 通过select @@servern ...

  5. runtime梳理。

    一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于 ...

  6. AES加密

    package com.edu.hpu; import java.math.BigInteger; import java.security.MessageDigest; import java.se ...

  7. Android业务组件化之子模块SubModule的拆分以及它们之间的路由Router实现

    前言: 前面分析了APP的现状以及业务组件化的一些探讨(Android业务组件化之现状分析与探讨),以及通信的桥梁Scheme的使用(Android业务组件化之URL Scheme使用),今天重点来聊 ...

  8. Hawk 4.7 单步调试

    单步调试的意义 已经编写的工作流,可能会因为某些外界环境的变化而出错,此时需要排除错误,我们可以使用单步调试. 单步调试的本质,相当于只使用前n个模块,这样就能看到每个步骤下,流的改变. 例子 还是上 ...

  9. 设置tomcat远程debug

    查看端口占用情况命令: netstat -tunlp |grep 8000 tomcat 启动远程debug: startup.sh 中的最后一行 exec "$PRGDIR"/& ...

  10. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...