2016-07-28 58 views
3

我對EF來說相當陌生,現在已經困擾了我幾天:如何獲得一個EF查詢來編譯最優化的SQL?

我有一個用戶實體。它有一個父WorkSpace,它有一組用戶。 每個用戶在User.Schedules屬性中也有一個Children Schedule的集合。

我瀏覽過的對象是這樣的:

var query = myUser.WorkSpace.Users.SelectMany(u => u.Schedules); 

當列舉的query結果(MYUSER是已使用.Find(用戶ID)先前加載用戶的實例),我注意到, EF會在WorkSpace中爲每個用戶對數據庫執行一次查詢。用戶

爲什麼EF無法從myUser的主鍵開始在單個查詢中獲取結果,並將其與所有涉及的表連接在一起?

如果我做別的事情直接從上下文中像這樣的,它工作正常,但:

context.Users.Where(u => u.ID = userid).SelectMany(u => u.WorkSpace.Users.SelectMany(u => u.Schedules)) 

有什麼我做錯了嗎?

+0

使用'.Join'讓EF理解你想要達成的目標 –

+0

但是我會加入什麼?我想我會在我的背景下加入一些東西,所以和第二個例子沒什麼兩樣,是嗎? –

+0

我可能不是最好解決這個問題,因爲我不使用導航屬性,我保持清楚。我只是一直使用鍵和連接,如果查詢變得更加複雜,我使用Dapper或其他東西在代碼存儲過程中啓動 –

回答

2

我們採取的第一個查詢:

var query = myUser.WorkSpace.Users.SelectMany(u => u.Schedules); 

如果你看一下query變量的類型,你會看到,它是IEnumerable<Schedule>,這意味着這是一個普通的LINQ to Objects查詢。爲什麼?因爲它從物化對象開始,然後加入另一個對象/集合等。這與EF延遲加載功能相結合導致了多數據庫查詢行爲。

如果第二查詢做同樣的:

var query = context.Users.Where(u => u.ID = userid) 
    .SelectMany(u => u.WorkSpace.Users.SelectMany(u => u.Schedules)) 

,你會發現,query的類型現在是IQueryable<Schedule>,這意味着,你現在的LINQ to Entities查詢。這是因爲在查詢中使用的context.Users和其他對象/集合都不是真實對象 - 它們只是用於構建,執行和實現查詢的元數據。

回顧一下,你沒有做錯什麼。懶加載以這種方式工作。如果您不在意所謂的N+1 query問題,則可以使用第一種方法。如果你真的在乎,那就用第二個。

+2

關鍵部分是EF Linq查詢基於'Expression '謂詞,它們不是編譯表達式。 EF會讀取這些表達式樹來創建SQL。因爲我們每行都使用IEnumerable <>和yield,所以EF爲每一行都創建一個新的查詢,因爲它沒有整個上下文的知識 –

+0

好的謝謝,看起來很清楚,我想我現在明白爲什麼EF無法理解我在第一個示例中要做的事情。我關心N + 1問題(我有相當大量的兒童記錄,我試圖儘快檢索),看起來像我沒有選擇,只能在我的代碼中使用上下文 –