LINQ实体Where子句不在正确的位置

时间:2018-02-02 15:39:36

标签: asp.net sql-server entity-framework linq linq-to-entities

显然我错过了LINQ to entities如何工作的东西。希望你们中的一个能够教育我。

请在本地尝试以下操作,如果您看到相同的结果,请与我们联系。这里真的很奇怪......

让我们使用导航属性查看一个非常简单的LINQ表达式。

这是在LinqPad的C#语句中生成的。

var result = (from ge in group_execution
                where ge.automation_sequences.project.client_id == 1 && ge.parent_group_exec_id != null
                select new
                {
                    ge.id,
                    ge.parent_group_exec_id,
                    ge.automation_sequences.project.client_id
                });
result.Dump();

或者,我们可以使用连接...这将导致相同的不良结果,但让我们继续......

var result = (from ge in group_execution
            join aseq in automation_sequences on ge.automation_sequence_id equals aseq.id
            join p in project on aseq.project_id equals p.id
            where p.client_id == 1 && ge.parent_group_exec_id != null
            select new
            {
                ge.id,
                ge.parent_group_exec_id,
                p.client_id
            });
result.Dump();

这些非常简单的LINQ表达式生成以下SQL:

SELECT 
    [Filter1].[id1] AS [id], 
    [Filter1].[parent_group_exec_id] AS [parent_group_exec_id], 
    [Extent5].[client_id] AS [client_id]
    FROM    (SELECT [Extent1].[id] AS [id1], [Extent1].[automation_sequence_id] AS [automation_sequence_id], [Extent1].[parent_group_exec_id] AS [parent_group_exec_id]
        FROM   [dbo].[group_execution] AS [Extent1]
        INNER JOIN [dbo].[automation_sequences] AS [Extent2] ON [Extent1].[automation_sequence_id] = [Extent2].[id]
        INNER JOIN [dbo].[project] AS [Extent3] ON [Extent2].[project_id] = [Extent3].[id]
        WHERE ([Extent1].[parent_group_exec_id] IS NOT NULL) AND (1 = [Extent3].[client_id]) ) AS [Filter1]
    LEFT OUTER JOIN [dbo].[automation_sequences] AS [Extent4] ON [Filter1].[automation_sequence_id] = [Extent4].[id]
    LEFT OUTER JOIN [dbo].[project] AS [Extent5] ON [Extent4].[project_id] = [Extent5].[id]

这令我感到困惑。对于我的生活,我无法理解为什么LINQ会这样做。这太可怕了,只看一下执行计划:

enter image description here

现在让我们在SSMS中手动清理它并查看正确的SQL和执行计划:

enter image description here

好多了,但我们如何让LINQ以这种方式行事呢?

还有其他人看到这个吗?有没有人见过这个并纠正过它,如果是这样的话?

感谢您对此进行调查。

更新,尝试Chris Schaller修复:

var result = (from ge in group_execution
                select new
                {
                    ge.id,
                    ge.parent_group_exec_id,
                    ge.automation_sequences.project.client_id
                }).Where(x=>x.client_id == 1 && x.parent_group_exec_id != null);
result.Dump();

大家都知道我通过SQL Server Profiler监控SQL。如果有人知道这样做有什么问题,请告诉我。

enter image description here

UPDATE,对JOINS的修复,但不是导航属性,原因,但为什么?

这是您的解决方案:

var result = (from ge in group_execution.Where(x=>x.parent_group_exec_id != null)
            join aseq in automation_sequences on ge.automation_sequence_id equals aseq.id
            join p in project on aseq.project_id equals p.id
            where p.client_id == 1// && ge.parent_group_exec_id != null
            select new
            {
                ge.id,
                ge.parent_group_exec_id,
                p.client_id
            });
result.Dump();

enter image description here

空检查不应该导致框架像这样陷入困境。我为什么要用这种方式写呢?这对我来说似乎是一个缺陷。这将使我的动态表达式更难写,但也许我可以找到一种方法。

导航属性仍然​​搞乱......所以我仍然很难过。图片如下:

var result = (from ge in group_execution.Where(x=>x.parent_group_exec_id != null)
                where ge.automation_sequences.project.client_id == 1// && ge.parent_group_exec_id != null
                select new
                {
                    ge.id,
                    ge.parent_group_exec_id,
                    ge.automation_sequences.project.client_id
                });
result.Dump();

enter image description here

2 个答案:

答案 0 :(得分:0)

在定义了select语句

的结构之后将where子句移动到
var result = (from ge in group_execution
            select new
            {
                ge.id,
                ge.parent_group_exec_id,
                ge.automation_sequences.project.client_id
            }).Where(x => x.client_id == 1 && x.parent_group_exec_id != null)
result.Dump();

答案 1 :(得分:0)

  

请记住,Linq-to-entities将查询结果展平为SQL,然后从这些结果中保存对象图。

当您的查询使用导航属性或连接时,查询解析器必须允许来自这些子查询( Extents )的零结果,以确保输出中所需的所有列和表示任何临时处理。通过在查询的早期在表上显式指定过滤器!= null,解析器知道该字段和该字段链接的任何关系不再有可能为空,直到那时解析器准备查询就像连接一样将返回null结果

  

值得检查,但我想知道 UseDatabaseNullSemantics 是否与此有关?

尝试:

dbContext.Configuration.UseDatabaseNullSemantics = false

在Linq中,我们可以随意指定Where子句,改进我们应该在查询的早期过滤的结果SQL。

解析器引擎经过优化,可以按顺序跟踪和实现查询,并在结束时生成良好的SQL。不要尝试以与构造SQL相同的方式编写linq-to-entities,我知道这是反直觉的,因为语法类似

  

一种好的技术是假设在每个子句之前,前面语句中的所有记录都已加载到内存中,并且下一个操作将影响所有这些记录。因此,您希望在继续执行下一个子句之前通过指定过滤器来减少每个附加操作之前的记录

通常,如果您有基于根表的过滤条件,请在定义所有其他连接和过滤器之前将其应用于查询,甚至选择,您将获得更清晰的SQL。