Linq to entities Left Join

Mark picture Mark · Oct 1, 2009 · Viewed 11.2k times · Source

I want to achieve the following in Linq to Entities:

Get all Enquires that have no Application or the Application has a status != 4 (Completed)

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

Has anyone done this before without using DefaultIfEmpty(), which is not supported by Linq to Entities?

I'm trying to add a filter to an IQueryable query like this:

IQueryable<Enquiry> query = Context.EnquirySet; 

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

Thanks Mark

Answer

Mark Duff picture Mark Duff · Aug 1, 2012

In EF 4.0+, LEFT JOIN syntax is a little different and presents a crazy quirk:

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 
        } 

If you capture the execution of this query in SQL Server Profiler, you will see that it does indeed perform a LEFT OUTER JOIN. HOWEVER, if you have multiple LEFT JOIN ("Group Join") clauses in your Linq-to-Entity query, I have found that the self-join clause MAY actually execute as in INNER JOIN - EVEN IF THE ABOVE SYNTAX IS USED!

The resolution to that? As crazy and, according to MS, wrong as it sounds, I resolved this by changing the order of the join clauses. If the self-referencing LEFT JOIN clause was the 1st Linq Group Join, SQL Profiler reported an INNER JOIN. If the self-referencing LEFT JOIN clause was the LAST Linq Group Join, SQL Profiler reported an LEFT JOIN.