Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN

Posted

技术标签:

【中文标题】Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN【英文标题】:Linq-to-Entities: LEFT OUTER JOIN with WHERE clause and projection 【发布时间】:2011-04-26 23:24:10 【问题描述】:

我花了很长时间弄清楚如何将带有两个条件 where 子句的简单 SQL LEFT OUTER JOIN 转换为有效的 Linq-to-Entities 查询。只有两张桌子。我需要 Table1 中所有行的值,无论 Table2 中的匹配项如何,但 WHERE 子句使用 Table2 中的字段。在 SQL 中,这两个参数是 Table2WhereColumn1 和 Table2WhereColumn2,查询(有效)如下所示:

SELECT t1.Table1Id,
    t1.FieldDescription, 
    t2.FieldValue
FROM Table1 t1 WITH (NOLOCK)
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id
WHERE (t2.Table2WhereColumn1 = @someId OR t2.Table2WhereColumn1 IS NULL)
AND (t2.Table2WhereColumn2 = @someOtherId OR t2.Table2WhereColumn2 IS NULL)
ORDER BY t1.OrderByColumn

我尝试过将Group JoinDefaultIfEmpty() 一起使用,以及隐式连接(没有实际的Join 关键字),但我只获取表2 中具有值的项目的行。我确信这不会有帮助,但这是我一直在尝试但不起作用的 Linq 示例:

Public Shared Function GetProfilePreferencesForCedent(ByVal dc As EntityContext, _
                                                      ByVal where1 As Int32, _
                                                      ByVal where2 As Int32) _
                                                  As IQueryable(Of ProjectedEntity)
    Return From t1 In dc.Table1
           Group Join t2 In dc.Table2 _
                On t1.Table1Id Equals t2.Table1Id _
                Into t2g1 = Group _
           From t2gx In t2g1.DefaultIfEmpty(Nothing)
           Where (t2gx.Table2Where1 = where1 Or t2gx.Table2Where1 = Nothing) _
                And (t2gx.Table2Where2 = where2 Or t2gx.Table2Where2 = Nothing)
           Order By t1.SortOrder
           Select New ProjectedEntity With 
               .Table1Id = t1.Table1Id, _
               .FieldDescription = t1.FieldDescription, _
               .FieldValue = If(t2gx Is Nothing, String.Empty, t2gx.FieldValue) _
           
End Function

【问题讨论】:

【参考方案1】:

试试这些查询,告诉我它们是否适合您。我还没有设置要测试的数据,但它们应该没问题。

请原谅我混合使用 C# 和 VB.NET。我曾经是一名 VB.NET 开发人员,但在过去的几年中,我主要使用 C# 工作,所以我现在在那里感觉更舒服了。

这是我为Table1 & Table2 创建的类:

public class Table1

    public int Table1Id  get; set; 
    public string FieldDescription  get; set; 
    public int OrderByColumn  get; set; 

public class Table2

    public int Table1Id  get; set; 
    public string FieldValue  get; set; 
    public int Table2WhereColumn1  get; set; 
    public int Table2WhereColumn2  get; set; 

现在 C# 中的查询应该是:

var query =
    from t1 in Table1
    join t2 in Table2 on t1.Table1Id equals t2.Table1Id into _Table2
    from _t2 in _Table2.DefaultIfEmpty()
    where _t2 == null ? true :
        _t2.Table2WhereColumn1 == @someId
        && _t2.Table2WhereColumn2 == @someOtherId
    orderby t1.OrderByColumn
    select new
    
        t1.Table1Id,
        t1.FieldDescription,
        FieldValue = _t2 == null ? "" : _t2.FieldValue,
    ;

并翻译成VB.NET:

Dim query = _
    From t1 In Table1 _
    Group Join t2 In Table2 On t1.Table1Id Equals t2.Table1Id Into _Table2 = Group _
    From _t2 In _Table2.DefaultIfEmpty() _
    Where If(_t2 Is Nothing, True, _t2.Table2WhereColumn1 = someId AndAlso  _
                                   _t2.Table2WhereColumn2 = someOtherId) _
    Order By t1.OrderByColumn _
    Select New With  _
            .Table1Id = t1.Table1Id, _
            .FieldDescription = t1.FieldDescription, _
            .FieldValue = If(_t2 Is Nothing, "", _t2.FieldValue) _
        

让我知道它们是否有效。手指交叉。 :-)

【讨论】:

优秀!奇迹般有效。我不确定我是否理解 Where 子句的工作原理,但它确实有效。【参考方案2】:

就个人而言,如果存在左连接右侧的 where 条件,我通常更喜欢将它们放入连接条件中

在这种情况下,SQL 如下所示:

SELECT t1.Table1Id,
       t1.FieldDescription, 
       t2.FieldValue
FROM Table1 t1 WITH (NOLOCK)
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id 
                                  AND t2.Table2WhereColumn1 = @someId
                                  AND t2.Table2WhereColumn2 = @someOtherId
ORDER BY t1.OrderByColumn

用于此的 LINQ 代码(在 C# 中)如下所示:

var query =
    from t1 in Table1
    join t2 in Table2 on newa = t1.Table1Id, b = someId, c = someotherId 
                         equals new a = t2.Table1Id b = t2.Table2WhereColumn1, c = Table2WhereColumn2 
    into _Table2
    from _t2 in _Table2.DefaultIfEmpty()
    orderby t1.OrderByColumn
    select new
    
        t1.Table1Id,
        t1.FieldDescription,
        FieldValue = _t2 == null ? "" : _t2.FieldValue,
    ;

未测试 - 但应该可以工作

【讨论】:

+1 这是我喜欢生成 Linq 右表的方式,其中左外连接上的条件我觉得它读起来更类似于我习惯的 Sql 语法。 语法真的很好。我必须同意更像 SQL 的感觉。 如果在 JOIN 中使用 OR 条件,您如何执行上述操作?【参考方案3】:

我不会把这个答案归功于这个答案,但它很漂亮:LINQ to SQL - Left Outer Join with multiple join conditions

本质上,在子查询上使用扩展方法 where 子句,但必须在 DefaultIfEmpty() 之前使用它:

from p in context.Periods
join f in context.Facts on p.id equals f.periodid into fg
from fgi in fg.Where(f => f.otherid == 17).DefaultIfEmpty()
where p.companyid == 100
select f.value

【讨论】:

简洁大方,完美。

以上是关于Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN的主要内容,如果未能解决你的问题,请参考以下文章

实体框架 .Any 不生成预期的 SQL WHERE 子句

分组后的LINQ to Entities,COUNT和WHERE不起作用

为啥 LINQ-to-Entities 将此查询放在子选择中?

使用 LINQ-to-Entities 搜索时忽略空字符串

在 Linq-to-Entities 查询中格式化日期会导致异常

如果存在-UPDATE-else-INSERT 与 Linq-to-Entities?