2009-10-01 214 views
5

我想實現的LINQ以下到實體:LINQ到實體左連接

找那些沒有應用程序或應用程序有一個狀態所有諮詢= 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 

有任何人在不使用DefaultIfEmpty()之前完成此操作,Linq不支持實體?

我想一個過濾器添加到這樣一個IQueryable查詢:

IQueryable<Enquiry> query = Context.EnquirySet; 

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

感謝 馬克

+0

它值得什麼DefaultIfEmpty包含在EF .NET 4.0中。 – DamienG 2009-10-01 23:22:31

+0

感謝Damien - 期待.NET 4中的EF增強功能4 – Mark 2009-10-02 09:57:40

回答

6

這樣做:

IQueryable<Enquiry> query = Context.EnquirySet; 

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

我不覺得會有什麼的都在SQL「愚蠢的」,「外部聯接」問題的LINQ的處理。理解它的關鍵是用具有可空屬性的對象圖來思考,而不是表格結果集。

任何()都映射到SQL中的EXISTS,所以它在遠處在某些情況下比Count()更有效。

+0

非常好的幫助,謝謝! – Mark 2009-10-02 09:01:22

+0

Sweeeeeeet:)很好回答 – SDReyes 2010-04-03 19:20:49

1

的因爲LINQ的的高飛(讀非標)的處理,外表面的方式,你有使用DefaultIfEmpty()。

你要做的是將你的Linq-To-Entities查詢運行到兩個IEnumerables中,然後使用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到實體做到這一點並不意味着你不能與原始的LINQ做。

(注:有人downvotes我面前......是的,我知道有更優雅的方式做到這一點,我只是試圖使其理解這一點很重要的概念,對吧?)

+0

當我過濾IQueryable 結果以創建像這樣的存儲表達式時,可以這樣做嗎?: IQueryable query = Context.EnquirySet ; 查詢=(來自查詢中的e 其中e.Applications.DefaultIfEmpty()。其中​​(app => app.Status!= 4).Count()> = 1 \t select e); – Mark 2009-10-01 14:22:01

+0

我更新了這篇文章,讓代碼示例更清晰! – Mark 2009-10-01 14:24:15

+0

嗯,似乎無法得到這個工作在我的例子。你確定where子句是正確的嗎?狀態是在應用程序實體??? 謝謝 – Mark 2009-10-01 14:46:22

3

謝謝你們的幫助。我最終選擇了這個選項,但是您的解決方案有助於拓寬我的知識面。

IQueryable<Enquiry> query = Context.EnquirySet; 

query = query.Except(from e in query 
        from a in e.Applications 
        where a.Status == 4 
        select e); 
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中一樣 - 即使使用了上述語法!

該決議?根據MS的說法,這聽起來很不對勁,我通過改變連接子句的順序來解決這個問題。如果自引用LEFT JOIN子句是第一個Linq Group Join,則SQL事件探查器報告一個INNER JOIN。如果自引用LEFT JOIN子句是最後一個Linq組連接,則SQL事件探查器報告一個LEFT JOIN。

+0

這個答案應該真的得到更多的業力 – Thomas 2013-06-19 09:25:13

1

另一件需要考慮的事情是,如果您直接引用左連接組中的where子句中的任何屬性(使用into語法)而不檢查null,Entity Framework仍然會將您的LEFT JOIN轉換爲INNER JOIN。在匿名類型

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查詢:

爲了避免這種情況,過濾器對您的查詢的「從X在leftJoinedExtent」的一部分,像這樣加入。