Linq-to-Entities:使用WHERE子句和投影LEFT OUTER JOIN

时间:2010-10-18 14:03:45

标签: vb.net linq linq-to-entities left-join

我有一个heckuva时间弄清楚如何将一个简单的SQL LEFT OUTER JOIN转换为一个带有两个条件的where子句到一个工作的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

3 个答案:

答案 0 :(得分:10)

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

请原谅我的C#& amp; 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) _
        }

如果他们有效,请告诉我。手指交叉。 : - )

答案 1 :(得分:4)

就个人而言,如果左边连接的右边有条件,我通常更喜欢将它们加入连接标准

在这种情况下,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

这个(在C#中)的LINQ代码如下所示:

var query =
    from t1 in Table1
    join t2 in Table2 on new{a = 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,
    };

没有测试过 - 但是应该可以工作

答案 2 :(得分:0)

我不赞成这个答案,但它很华丽: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