Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq to entities Left Join

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

like image 791
Mark Avatar asked Oct 01 '09 14:10

Mark


People also ask

Can we use left join in LINQ?

You can use LINQ to perform a left outer join by calling the DefaultIfEmpty method on the results of a group join.

How do I join two LINQ queries?

LINQ Join queries. As we know the JOIN clause is very useful when merging more than two table or object data into a single unit. It combines different source elements into one and also creates the relationship between them. Using the join, you can grab the data based on your conditions.

Which join is valid in LINQ?

Join and GroupJoin are joining operators. Join is like inner join of SQL. It returns a new collection that contains common elements from two collections whosh keys matches. Join operates on two sequences inner sequence and outer sequence and produces a result sequence.

Is LINQ join inner or outer?

One commonly used feature of Language-Integrated Query (LINQ) is the facility to combine two sequences of related data using joins. The standard join operation provides an inner join but with a minor modification can be changed to give a left outer join.


3 Answers

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.

like image 150
Mark Duff Avatar answered Sep 18 '22 00:09

Mark Duff


Do this:

IQueryable<Enquiry> query = Context.EnquirySet; 

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

I don't find LINQ's handling of the problem of what would be an "outer join" in SQL "goofy" at all. The key to understanding it is to think in terms of an object graph with nullable properties rather than a tabular result set.

Any() maps to EXISTS in SQL, so it's far more efficient than Count() in some cases.

like image 34
Craig Stuntz Avatar answered Sep 21 '22 00:09

Craig Stuntz


Thanks guys for your help. I went for this option in the end but your solutions have helped broaden my knowledge.

IQueryable<Enquiry> query = Context.EnquirySet;

query = query.Except(from e in query
                     from a in e.Applications
                     where a.Status == 4
                     select e);
like image 32
Mark Avatar answered Sep 20 '22 00:09

Mark