2011-07-27 197 views
2

我有一些奇怪的行爲,我找不出來。IEnumerable/IQueryable擴展的奇怪行爲(沒有延遲加載?)

下面的兩個方法應該從我的理解(這顯然是錯誤的)給予IQueryable的行爲相同,但他們不這樣做。

如果我用IQueryable調用第一個對象(該對象是顯式用作IQueryable的實體框架中的DbSet),它看起來像不使用延遲加載(它對數據庫執行掃描)。當我用同一個對象調用第二個方法時,它看起來像我想要的那樣工作(它在數據庫上執行查找)。

於是,兩個問題:

  • 這究竟是爲什麼?

  • 我可以(以及如何)使最通用的方法(與IEnumerable)「正常」工作? (因爲我有更多的擴展和不想重複代碼,我想,以避免超載,只是複製粘貼的方法,身體像下文)

我使用EF 4.1針對SQL Server Express的工作2008數據庫

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(e => e.ID == id); 
} 

public static TEntity GetByID<TEntity>(this IQueryable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(e => e.ID == id); 
} 
+0

IEnumerable擴展方法無法按照您希望的方式工作。 – hatchet

回答

5

IEnumerable<T>未通過查詢表達式的EF LINQ提供,而是執行在存儲器中的SingleOrDefault()。這需要您的表格完整載入內存,然後是SingleOrDefault()。通過使用IQueryable<T>版本,提供者將被賦予正確的表達式樹,並將其轉換爲您想要的SQL。這就是差異所在。

+0

對。 SingleOrDefault是接口上的擴展方法。所以IEnumerable的實現與IQueryable完全不同。區別的線索是接口的名稱:IEnumerable枚舉集合(並且必須獲取整個集合來完成此操作),IQueryable使用表達式(如查詢)。 – hatchet

2

由於list是你的第一個案例類型爲IEnumerable實體框架查詢供應商將其視爲暗示在內存上執行SingleOrDefault()方法(它會使用LINQ的方法上Enumerable代替Queryable) - 這就是爲什麼你看到數據庫掃描實現完整列表。

還請參閱AsEnumerable()方法,並且此發佈者喬恩斯基特:"Reimplementing LINQ to Objects: Part 36 - AsEnumerable"

0

FirstOrDefault單獨爲IEnumerable和IQueryable定義 在第二種情況下調用System.Linq.Queryable.FirstOrDefault()。

如果您想將兩者結合到一個方法中,您可以測試列表是否爲IQueryable,並使用Queryable擴展方法代替。

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, long id) where TEntity : Identifiable 
{ 
    var query = list as IQueryable<TEntity>; 
    if (query != null) 
     return query.SingleOrDefault(e => e.ID == id); 
    return list.SingleOrDefault(e => e.ID == id); 
} 
0

對於任何人誰到這裏來,一兩件事要注意:

你需要絕對確保你提供的是表達SingleOrDefault()方法,否則有潛力以下發生:

public static TEntity GetByID<TEntity>(this IEnumerable<TEntity> list, Func<TEntity,bool> selector) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(selector); 
} 

public static TEntity GetByID<TEntity>(this IQueryable<TEntity> list, Func<TEntity,bool> selector) where TEntity : Identifiable 
{ 
    return list.SingleOrDefault(selector); 
} 

這些方法將運行完全相同。爲什麼?由於您未提供表達式IQueryable<>列表,實際上會發生什麼情況是列表將自動轉換爲IEnumerable<>,然後選擇器將在IEnumerable<>上運行。所以,實際上,您實際上是將整個表/列表從數據庫中拉出到內存中,然後在內存中的列表上運行選擇器。

我剛剛完成了這個,我雖然我瘋了。

如果你將Expression<Func<TEntity,bool>>傳遞給IQueryable<>,它將在DB端執行。