在应用程序中使用 LINQ to SQL 或 LINQ to Entities 时,有必要考虑对您 创建并重复执行的任何查询进行预编译。我经常在埋头苦干一项特定任务时忘了 利用预编译查询,等我意识到时为时已晚。这很像“异常处理病”,即开发人员 试图在事发后将异常处理强行加入应用程序中。
然而,即使您已经实施了此项重要的性能增强方法,往往也只是徒劳。您可 能会发现预期的性能增强并未实现,但原因(和解决方法)可能仍悬而未决。
在本篇专栏文章中,我首先将解释如何预编译查询,然后将重点讲述在 Web 应用程序、服务和其他方案中导致预编译无用的原因。您将学习如何确保在回发 、短期服务操作以及其他会导致关键实例超出作用域的代码中获得性能优势。
预编译查询
在庞大的查询执行中,将 LINQ 查询转换为相关的存储查询(例如,数据库 执行的 T-SQL)需要较高的成本。图 1 显示了将 LINQ to Entities 查询转换 为存储查询时涉及的流程。
图 1 将 LINQ 查询转换为相关的存储查询
实体框架团队的博客文章“探讨 ADO.NET 实体框架的性能 – 第 1 部分” (blogs.msdn.com/adonet/archive/2008/02/04/exploring-the-performance- of-the-ado-net-entity-framework-part-1.aspx) 对该流程进行了细分,并提 供了每个步骤的对应时间。注意:此篇文章基于 Microsoft .NET Framework 3.5 SP1 版本的实体框架,新版本中各步骤的时间分配可能发生了变化。但查询 执行流程中的预编译成本仍然较高。
通过预编译查询,实体框架和 LINQ to SQL 可以重复使用存储查询并跳过每 次计算的冗余流程。例如,如果您的应用程序经常从数据存储中检索不同的客户 ,您可能会有一个与下面类似的查询:
Context.Customers.Where(c=>c.CustomerID==_custID)
在从一个执行进入下一个执行时,如果只有 _custID 参数发生了变化,为什 么要浪费时间将此查询反复转置到 SQL 命令呢?
LINQ to SQL 和实体框架都启用了查询预编译,但由于这两个框架中的流程 不同,它们的 CompiledQuery 类也不同。LINQ to SQL 使用 System.Data.LINQ.CompiledQuery,而实体框架使用 System.Data.Objects.CompiledQuery。这两种形式的 CompiledQuery 都允许您 传入参数,而且都要求您传入当前正在使用的 DataContext 或 ObjectContext 。从编码的角度来说,它们在本质上是相同的。
CompiledQuery.Compile 方法以 Func 的形式返回一个委托,在需要时可以 调用该委托。
以下是实体框架的 CompiledQuery 类编译的一个简单查询,由于是静态查询 ,因此不要求实例化:
C#
var _custByID = CompiledQuery.Compile<SalesEntities, int, Customer>
((ctx, id) =>ctx.Customers.Where(c=> c.ContactID == id).Single());
VB
Dim _custByID= CompiledQuery.Compile(Of SalesEntities, Integer, Customer)
(Function(ctx As ObjectContext, id As Integer)
ctx.Customers.Where(Function(c) c.CustomerID = custID).Single)