Linq到实体Left Join

时间:2009-10-01 14:09:48

标签: linq linq-to-entities left-join outer-join

我想在Linq to Entities中实现以下功能:

获取所有没有应用程序或应用程序具有状态的查询!= 4(已完成)

select e.*
from Enquiry enq
left outer join Application app
 on enq.enquiryid = app.enquiryid
where app.Status <> 4 or app.enquiryid is null

有没有人在不使用Linq to Entities不支持的DefaultIfEmpty()之前完成此操作?

我正在尝试将过滤器添加到IQueryable查询中,如下所示:

IQueryable<Enquiry> query = Context.EnquirySet; 

query = (from e in query 
         where e.Applications.DefaultIfEmpty()
                             .Where(app=>app.Status != 4).Count() >= 1 
         select e);

由于 马克

5 个答案:

答案 0 :(得分:10)

在EF 4.0+中,LEFT JOIN语法略有不同,呈现出一种疯狂的怪癖:

var query = from c1 in db.Category 
        join c2 in db.Category on c1.CategoryID equals c2.ParentCategoryID  
        into ChildCategory 
        from cc in ChildCategory.DefaultIfEmpty() 
        select new CategoryObject  
        { 
            CategoryID = c1.CategoryID,  
            ChildName = cc.CategoryName 
        } 

如果在SQL Server Profiler中捕获此查询的执行,您将看到它确实执行了LEFT OUTER JOIN。但是,如果在Linq-to-Entity查询中有多个LEFT JOIN(“Group Join”)子句,我发现自连接子句实际上可以像INNER JOIN一样执行 - 即使使用了上面的语法! / p>

对此的决议?疯了,据MS说,听起来不对,我通过改变连接子句的顺序来解决这个问题。如果自引用LEFT JOIN子句是第一个Linq Group Join,则SQL事件探查器报告了一个INNER JOIN。如果自引用LEFT JOIN子句是LAST Linq Group Join,则SQL事件探查器会报告LEFT JOIN。

答案 1 :(得分:6)

这样做:

IQueryable<Enquiry> query = Context.EnquirySet; 

query = (from e in query 
         where (!e.Applications.Any()) 
               || e.Applications.Any(app => app.Status != 4)
         select e);

我没有发现LINQ处理SQL“goofy”中的“外连接”问题。理解它的关键是根据具有可空属性的对象图而不是表格结果集进行思考。

Any()映射到SQL中的EXISTS,因此在某些情况下,比Count()效率更高。

答案 2 :(得分:3)

谢谢你们的帮助。我最终选择了这个选项,但是你的解决方案有助于扩大我的知识。

IQueryable<Enquiry> query = Context.EnquirySet;

query = query.Except(from e in query
                     from a in e.Applications
                     where a.Status == 4
                     select e);

答案 3 :(得分:1)

由于Linq处理outers的愚蠢(阅读非标准)方式,你必须使用DefaultIfEmpty()。

你要做的是将你的Linq-To-Entities查询运行到两个IEnumerables中,然后LEFT使用DefaultIfEmpty()加入它们。它可能看起来像:

IQueryable enq = Enquiry.Select();
IQueryable app = Application.Select();
var x = from e in enq
join a in app on e.enquiryid equals a.enquiryid
into ae
where e.Status != 4
from appEnq in ae.DefaultIfEmpty()
select e.*;

仅仅因为你不能用Linq-To-Entities做到这一点并不意味着你不能用原始的Linq做到这一点。

(注意:在任何人投票给我之前......是的,我知道有更优雅的方法可以做到这一点。我只是想让它变得可以理解。这个概念很重要,对吗?)

答案 4 :(得分:1)

另外需要考虑的是,如果直接从左连接组中引用where子句中的任何属性(使用into语法)而不检查null,则Entity Framework仍会将LEFT JOIN转换为INNER JOIN。

要避免这种情况,请过滤查询中的“from x in leftJoinedExtent”部分,如下所示:

var y = from parent in thing
        join child in subthing on parent.ID equals child.ParentID into childTemp
        from childLJ in childTemp.Where(c => c.Visible == true).DefaultIfEmpty()
        where parent.ID == 123
        select new {
            ParentID = parent.ID,
            ChildID = childLJ.ID
        };

匿名类型中的ChildID将是可以为空的类型,并且生成的查询将是LEFT JOIN。