【问题标题】:Linq2Entities CompiledQuery for query that uses joinsLinq2Entities CompiledQuery 用于使用连接的查询
【发布时间】:2012-04-12 01:35:45
【问题描述】:

我的查询表现不佳,例如生成的 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 Profiler,发现生成的 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 返回匿名类型。

【问题讨论】:

    标签: c# entity-framework entity-framework-4 linq-to-entities


    【解决方案1】:

    我发现使用 CompiledQueries 的解决方法是:

    1. 在每个使用 LINQ to Entity 的 QueryX() 方法之前添加一个私有 InitQueryX() 方法。
    2. 使用属性和反射从 Init() 方法调用所有 InitQueryX() 方法。
    3. 在应用启动时调用一次 Init() 方法。

    这会在开始时强制编译查询,但可以以比 CompiledQueries 更灵活的方式编写查询。

    InitQueryX() 应该使用多个虚拟输入,以便它覆盖 QueryX() 方法中的所有路径(有点像单元测试代码覆盖率)。

    如果可能,InitQueryX() 的输入应该是在数据库中产生 0 行的模拟,这样 Init() 方法将花费更少的时间来运行。

    【讨论】:

      【解决方案2】:

      如果您的返回对象将具有 8 个或更少的属性,您可以使用 Tuple 类。如果您有更多属性并且不想为这些属性声明一个类,您可以使用dynamic 作为返回类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 1970-01-01
        • 2021-05-22
        • 2020-08-18
        • 2011-07-20
        • 1970-01-01
        相关资源
        最近更新 更多