如何防止EF-Linq为变量与常量生成不同的查询?

时间:2013-04-04 16:27:07

标签: performance linq entity-framework query-optimization

我有一个Linq查询,它使用硬整数值在大约一分钟内(第一次)在LinqPad中返回。 但是,当我将这些硬整数值转换为局部变量时,它需要永远(超过20分钟)。

我的基本问题是:

  • 为什么/不同的SQL查询会导致这么多延迟?
  • 我可以阻止这种不同的SQL查询吗?

在我试图解决这个问题时(见下文),我遇到了这些问题:

  • 如何使用DefaultQueryPlanCachingSettingEnablePlanCaching
  • 如何抑制/阻止查询编译/优化?
  • 如何验证我是否使用EF5?

query-difference似乎已经被注意到,而why is explained则回答了这个问题。我也有这个查询差异,我看到一个额外的子SELECT(和一些额外的JOIN)。查询使用参数([Extent6].[SomeThingId] = @p__linq__0)代替硬整数值(4 = [Extent6].[SomeThingId]

但是,我不确定为什么这会是一个问题,我怎么能阻止它。 (我知道在提供查询或数据库布局时,可能更容易,但这种敏感材料,我的问题仍然相同......)

我在使用参数时假设问题来自编译/执行计划中的某些'优化'。我正在尝试使用this suggestion to turn of the auto-compilation for L2E

 db.ContextOptions.DefaultQueryPlanCachingSetting = false;
 //(db as IObjectContextAdapter).ObjectContext.DefaultQueryPlanCachingSetting = false;

我无法让这个工作,所以我试图找出是否可以使用也提到的[ObjectQuery.EnablePlanCaching = false;],但我找不到在哪里/如何使用它。 (我无法将IQueryable转换为ObjectQuery,也不能投放到我的上下文。)有人知道如何使用DefaultQueryPlanCachingSettingEnablePlanCaching吗?

我尝试的另一件事是从常量值Linq中获取SQL,并在其中引入一些SQL变量。这也很好,所以我尝试在存储过程中转换它,我可以从EF调用。但是现在存储过程也需要很长时间才能运行。

我(也)想知道if I really have EntityFramework 5。 (这是我使用Linq,EntityFramework和ASP.MVC的第一个项目。)但我已经在另一个问题中解决了这个问题。

更新:我更改了“常规”存储过程:

SELECT ... WHERE @Id  = [Extent6].[SomeThingId] ...

使用动态SQL语句进入存储过程:

@sql = 'SELECT ... WHERE ' + CAST(@Id AS VARCHAR) + ' = [Extent6].[SomeThingId] ...';
EXEC (@sql);

通过这种“动态”存储过程,我可以获得非常快速的结果。而且由于参数将相当稳定(它们可能会在每天一次和每周一次之间变化),我认为缺乏优化/缓存对于性能来说是可行的。 (但是,我不喜欢将业务逻辑放在存储过程中。)

1 个答案:

答案 0 :(得分:1)

我发现Stuart Leeks关于Entity Framework 5 controlling automatic query compilation的这篇文章。它提到DefaultQueryPlanCachingSetting没有超过EF5的Release Candidate,你需要在ObjectSet上设置EnablePlanCaching属性。它还有一个简单的例子和​​一个方便的扩展方法的建议。我引用Stuart Leeks的例子,因为'常规'MSDN还没有提供。您可以在他的博客上查看扩展方法。

ObjectQuery<Customer> customersNoCache = context.Customers;
customersNoCache.EnablePlanCaching = false;
var query1 = from customer in context.Customers
             where customer.Country == "UK"
             select customer;

修改:实际上我必须添加更多代码to get from a DbSet to an ObjectQuery。与之前的Customer一致 - 示例如下:

ObjectContext lObjectContext = ((IObjectContextAdapter)this).ObjectContext;
ObjectQuery<Customer> lDocCatgNoCache = lObjectContext.CreateObjectSet<Customer>();

但生成的SQL仍然包含参数优化。我的猜测是EnablePlanCaching只抑制从Linq生成SQL的缓存,它甚至不考虑SQL端的缓存?

(不幸的是,我没有时间进行再研究/测试。我接着实施了'动态'存储过程,并完成了这个项目。)