或者Linq外连接多列

时间:2012-09-27 12:18:43

标签: linq linq-to-entities

我编写了一个Linq查询来对多个列进行外连接。我的问题是我想要OR列。换句话说,我需要连接条件:

on Bar.Id equals Foo.BarId1 or Bar.Id equals Foo.BarId2

我在本网站上看到的一些例子使用了匿名类型来创建AND条件,但我无法弄清楚如何使用匿名类型构建OR,或者甚至可能。我找不到匹配的例子。

我的查询:

    var data = (from f in Foos
                join b in Bars on f.BarId1 equals b.Id into tb
                  from xb in tb.DefaultIfEmpty()
                join b in Bars on f.BarId2 equals b.Id into tb2
                  from xb2 in tb2.DefaultIfEmpty()
                select new { Foo = f, Bar1 = xb, Bar2 = xb2 });

这有效,但我觉得这不是最好的解决方案

修改

我说得太早了。它不起作用:当Foo.BarId1指向有效的Bar,而Foo.BarId2不指向时,它实际上返回Bar1和Bar2的相同Bar。我希望Bar1有效,Bar2在这种情况下为空。

修改

我越来越近了。这是我最新的询问:

var data = from f in Foos
            from b in Bars.Where(x => x.Id == p.BarId1 || x.Id == p.BarId2).DefaultIfEmpty()
            select new { Foo = p, Bars = b };

我期待它回归:

Foo Bar    
f1  b1      
    b2

我得到的是:

Foo Bar
f1  b1
f1  b2

修改

我终于找到了完全返回我想要的查询:

var data = from f in Foos
       select new
       {
           Foo = f,
           Bar1 = Bars.FirstOrDefault(x => x.Id == f.Bar1Id),
           Bar2 = Bars.FirstOrDefault(x => x.Id == f.Bar2Id)
       };

我还想知道我可以对此做出哪些改进。

最终编辑

我已回到原始查询:

    var data = (from f in Foos
                join b in Bars on f.BarId1 equals b.Id into tb
                  from xb in tb.DefaultIfEmpty()
                join b in Bars on f.BarId2 equals b.Id into tb2
                  from xb2 in tb2.DefaultIfEmpty()
                select new { Foo = f, Bar1 = xb, Bar2 = xb2 });

事实证明这实际上是有效的,但我的Linqpad测试中的一个错误让我觉得它不是。

它也比前一个更有效 - 在SQL事件探查器中我可以看到它生成了1个SQL选择,与之前相比,它产生3 * n个选择(每个Foo选择都有2个Bar选择)

3 个答案:

答案 0 :(得分:0)

如以下链接所示, LINQ 仅支持支持equal。如果您需要任何其他类型的联接,请使用交叉联接,其中:

Linq - left join on multiple (OR) conditions

答案 1 :(得分:0)

LINQ还通过将连接放在Where子句中来支持ANSI-82语法。看看以下内容是否适合您:

var data = from f in Foos
           from b in Bars
           where f.Id == b.BarId1 || f.Id == b.BarId2
           select new { Foo = p, Bars = bx };

我怀疑这只会给你一个内连接而不是外连接。你可能需要添加另一个where子句!包含根据你的要求评估外部条件。

答案 2 :(得分:0)

如果您愿意引入导航属性Foo.Bar1Foo.Bar2,则可以非常简单。然后你可以这样做:

from f in Foos
select new { f, f.Bar1, f.Bar2 }

这将在SQL中创建外连接。 (这可能是不喜欢这些导航属性的原因,因为很容易忘记它们会导致外连接,如果没有它们,你总是被要求自己控制)。