LINQ to Entity:多个联接条件

47

有许多关于LINQ和多个连接的帖子。 然而,我没有找到任何解决我想要进行的连接的方法。

SQL等效语句可能如下:

SELECT * FROM table1 a
LEFT JOIN table2 b ON a.col1 = b.key1 AND
a.col2 = b.key2 AND
b.from_date <= now() AND
b.deleted = 0;

这是我尝试过的众多Linq查询之一。

var query = (from x in context.table1
             join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2} 
             into result
             from result......

如何添加日期和删除标记的额外条件?如果使用 .Where 条件,则这将被视为内连接,而不是左连接。

5个回答

85

另一种方式可能是这样的:

var query = (from x in context.table1 
             join y in context.table2 on 
             new  {
                  Key1 = x.col1, 
                  Key2 = x.col2,
                  Key3 = true,
                  Key4 = true
                 }
             equals
             new {
                  Key1 = y.key1, 
                  Key2 =  y.key2,
                  Key3 = y.from_date< DateTime.Now,
                  Key4 = !y.deleted
                 }  
             into result
from r in result.DefaultIfEmpty()
select new  {x.Something, r.Something}

8
如果有人偶然看到这个回答,我也想补充一下评论。两个对象的属性名称必须匹配,否则编译器会报类型干扰的错误。但是可以通过像上面示例中显示设置属性名称来克服这个问题。 - Max Carroll

18

LINQ 支持连接语法和较旧的 ANSI-82 WHERE 语法。使用后者,您可以在内部联接中完成您想要的操作:

var nowTime = DateTime.Now;
var query = from a in context.table1
            from b in context.table2
            where a.col1 == b.key1
                 && a.col2 == b.key2 
                 && b.from_date < nowTime
                 && b.deleted == false
            select ???;

对于外连接,我更喜欢使用where和select many结合的语法。(请注意,LINQ查询中的顺序不需要模仿在SQL中执行操作的顺序,而且顺序更加灵活。)

var nowTime = DateTime.Now;
var query = from b in context.table2
            from a1 in a.Where(a2 => 
                b.key1 = a.col && 
                b.key2 = a.col2 &&
                b.from_date < nowTime &&
                b.deleted == false).DefaultIfEmpty()
            select ???;

1
ANSI-82的WHERE语法正在生成交叉连接。 - shrutyzet
@shrutyzet 这是使用 EF 6 还是 EF Core? - Jim Wooley
EF 6,也许你忘记添加.DefaultIfEmpty()。 - shrutyzet
1
我在原始帖子中漏掉了左外连接。您可以使用我在此博客文章中描述的 ANSI-82 语法来进行左外连接。 - Jim Wooley
第一个 Linq 生成了一个交叉连接,而不是内部连接。在 EF Core 3.1 中进行了测试。 - Kasun Koswattha
显示剩余2条评论

9

我在匿名对象的属性命名方面遇到了问题:

var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
                    es => new { es.Id, 9 },
                    eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
                    (es, eq) => new { es.Id, eq.Id }
                ).ToList();

编译器不满意,所以上面的答案帮助我找出了问题,并且这是我的工作解决方案。花了我一些时间才发现愚蠢的错误 :)

var subscriptions = context.EmailSubscription.Join(context.EmailQueue,
                    es => new { EmailSubscriptionId = es.Id, EmailTemplateId  = 9 },
                    eq => new { eq.EmailSubscriptionId, eq.EmailTemplateId },
                    (es, eq) => new { es.Id, eq.Id }
                ).ToList();

2
希望我能得到更详细的解释。 - Max Alexander Hanna
1
这是efcore联接的首选语法。在上面的示例中,es和eq是别名。它们被传输到一个新的匿名对象中,具有像EmailSubscriptionId这样的任意属性,该属性赋值为es.Id所引用的值。 - JakeMc

3
你可以使用第二个查询来过滤第一个结果集,这样可行吗?
var query = (from x in context.table1 
             join y in context.table2 on new {x.col1, x.col2} equals {b.key1, b.key2}  
             into result
query = from x in query
        where ...

这个可以吗?

1
除了@Muhammad Adeel Zahid的答案之外,您还可以使用一些其他条件,例如:
new
{
Key1 = ppl.PeopleId,
Key2 = true,
Key3 = true
}
equals
new
{
Key1 = y.PeopleId,
Key2 = !y.IsDeleted,
Key3 = (y.RelationshipType == 2 || y.RelationshipType == 4)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接