针对使用联接的查询的Linq2Entities CompiledQuery
本文关键字:查询 Linq2Entities CompiledQuery | 更新日期: 2023-09-27 18:29:11
我有一个查询执行得不太好,例如生成的SQL代码是次优的。
最初的声明看起来像这样(简化):
ctx.Table1.Where(t => ...)
.OrderBy(t => ....)
.Select(t => new {Table1 = t,
SomeProperty = t.Table2.SomeProperty,
SomeProperty2 = t.Table2.SomeProperty2,
AnotherProperty = t.Table3.AnotherProperty,
...
}
我查看了SQL事件探查器,发现生成的SQL会多次加入同一个表,执行该语句大约需要1秒的时间。
然后,我将声明改写为以下内容:
from t in ctx.Table1
join t2 in ctx.Table2 on t.key equals t2.key into lt2
from t2 in lt2.DefaultIfEmpty()
join t3 in ctx.Table3 on t.key equals t3.key into lt3
from t3 in lt3.DefaultIfEmpty()
where t ...
orderby t...
select new {Table1 = t, .... }
这生成了一个更好的语句,当从SQL探查器中获取并在Management studio中执行时,它的速度是上一个示例中代码生成的语句的两倍。
然而,当运行第二个示例中的代码时,EF生成表达式所花费的时间远远超过了从查询优化中获得的时间。
那么,我该如何将第二条语句写成CompiledQuery
呢。我基本上不知道如何从CompiledQuery
返回匿名类型。
我为使用CompiledQueries找到的一个变通方法是:
- 在使用LINQ to Entity的每个QueryX()方法之前添加一个私有InitQueryX(()方法
- 使用属性和反射从Init()方法调用所有InitQueryX()方法
- 在应用程序启动时调用Init()方法一次
这强制在一开始就编译查询,但允许以比CompiledQueries更灵活的方式编写查询。
InitQueryX()应该使用多个伪输入,以便覆盖QueryX(()方法中的所有路径(有点像单元测试代码覆盖率)。
如果可能的话,InitQueryX()的输入应该是在数据库中产生0行的mock,这样Init()方法运行所需的时间就会更少。
如果返回对象的属性不超过8个,则可以使用Tuple
类。如果您有更多的属性,并且不想为这些属性声明类,则可以使用dynamic
作为返回类型。