2016-02-13 141 views
0

查詢使用實體框架6.實體框架生成嵌套集合

假設我有兩個嵌套集合ICollection<Child>ICollection<Child2>實體Parent。我想預先抓取兩種:

dbContext.Parent.Include(p => p.Child).Include(p => Child2).ToList()

這會產生一個很大的查詢,它看起來像這樣在較高的水平:

SELECT ... FROM (
    SELECT (parent columns), (child columns), NULL as (child2 columns) 
    FROM Parent left join Child on ... 
    WHERE (filter on Parent) 
    UNION ALL 
    SELECT (parent columns), NULL as (child columns), (child2 columns) 
    FROM Parent left join Child2 on ... 
    WHERE (filter on Parent) 
)) 

有沒有辦法讓實體框架的行爲類似於批在NHibernate(或JPA,EclipseLink,Hibernate等)中獲取,你可以指定你想先查詢父表,然後分別爲每個子表查詢?

SELECT ... from Parent -- as usual 
SELECT ... from Child where parent_id in (list of IDs) 
SELECT ... from Child2 where parent_id in (list of IDs) 
-- alternatively, you can specify EXISTS instead of IN LIST: 
SELECT ... from Child where exists (select 1 from Parent where child.parent_id = parent.id and (where clause for parent)) 

我覺得這更容易理解和推理,因爲它更類似於你手動編寫的SQL。此外,它還可以防止結果集中多餘的父錶行。另一方面,這是更多的往返旅行。

回答

0

我不認爲這是可能的實體框架,至少使用LINQ。在一天結束時,ORM會嘗試生成最有效的查詢,至少可以。這就是說像實體這樣的ORM並不總是生成最好看的SQL或最有效的。我的猜測,這只是一個猜測,實體正試圖減少旅行次數和I/O因爲I/O是經驗,相對性。

如果您正在尋找對您的SQL進行細粒度控制,我建議您避免使用ORM或像我一樣使用Entity進行基本的CRUD和簡單查詢,爲複雜查詢使用存儲過程,例如複雜報告。總是有ADO.NET,但似乎你更傾向於使用ORM。

你可能會覺得這很有用。基本上沒有太多的調整可用。 https://stackoverflow.com/a/22390400/2272004

0

實體框架錯過了NHibernate提供的許多複雜功能。 EF的獨特賣點是其多功能的LINQ支持,但如果您需要聲明性控制ORM如何獲取跨多個表的數據,則EF不是選擇的工具。藉助EF,您只能嘗試找到程序技巧。

讓我說明通過展示你需要做的,以實現「批量抓取」與EF什麼:

context.Configuration.LazyLoadingEnabled = false; 
context.Children1.Where(c1 => parentIds.Contains(c1.ParentId)).Load(); 
context.Children2.Where(c2 => parentIds.Contains(c2.ParentId)).Load(); 
var parents = dbContext.Parent.Where(p => parentIds.Contains(p.Id)).ToList(); 

這將加載所需的數據進入到環境和EF連接通過家長和孩子關係修復。結果是一個parents列表與他們的兩個子集合填充。但當然,它有幾個缺點:

  • 您需要禁用延遲加載,因爲即使子集合被填充,它們也不會被標記爲加載。訪問它們仍然會在啓用時觸發延遲加載。
  • 重複性代碼:您需要重複謂詞三次。避免這種情況並不容易。
  • 太具體。對於每種不同的情況,即使它們幾乎相同,也必須編寫一組新的語句。或者使其可配置,這仍然是一個程序化解決方案。
  • EF的當前主生產版本(6)沒有查詢批處理功能。您需要第三方工具,如EntityFramework.Extended,以在一次數據庫往返中運行這些查詢。