2011-03-04 28 views
12

是否有一個(邏輯/性能)差異寫作:LINQ造型,鏈接where子句VS和運營商

ATable.Where(x=> condition1 && condition2 && condition3)

ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)

我已經使用了前但意識到使用後者,我可以閱讀和複製某個查詢的某些部分,以便在其他地方使用。 有什麼想法?

回答

22

簡短的回答
你應該做你感受到什麼是更具可讀性和可維護性的應用程序既將評估同一個集合。

龍答案相當長

Linq到對象
ATable.Where(x=> condition1 && condition2 && condition3) 在這個例子中由於只有一個謂語聲明編譯器將只需要產生一名代表和一個編譯器生成的方法。
從反射器

if (CS$<>9__CachedAnonymousMethodDelegate4 == null) 
{ 
    CS$<>9__CachedAnonymousMethodDelegate4 = new Func<ATable, bool>(null, (IntPtr) <Main>b__0); 
} 
Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate4).ToList<ATable>(); 

編譯器生成的方法:

[CompilerGenerated] 
private static bool <Main>b__0(ATable m) 
{ 
    return ((m.Prop1 && m.Prop2) && m.Prop3); 
} 

正如你可以看到,只有一個呼叫到Enumerable.Where<T>與委託作爲預期的,因爲只有一個Where擴展方法。


ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)現在這個例子中產生更大量的代碼。

if (CS$<>9__CachedAnonymousMethodDelegate5 == null) 
    { 
     CS$<>9__CachedAnonymousMethodDelegate5 = new Func<ATable, bool>(null, (IntPtr) <Main>b__1); 
    } 
    if (CS$<>9__CachedAnonymousMethodDelegate6 == null) 
    { 
     CS$<>9__CachedAnonymousMethodDelegate6 = new Func<ATable, bool>(null, (IntPtr) <Main>b__2); 
    } 
    if (CS$<>9__CachedAnonymousMethodDelegate7 == null) 
    { 
     CS$<>9__CachedAnonymousMethodDelegate7 = new Func<ATable, bool>(null, (IntPtr) <Main>b__3); 
    } 
    Enumerable.Where<ATable>(Enumerable.Where<ATable>(Enumerable.Where<ATable>(tables, CS$<>9__CachedAnonymousMethodDelegate5), CS$<>9__CachedAnonymousMethodDelegate6), CS$<>9__CachedAnonymousMethodDelegate7).ToList<ATable>(); 

因爲我們有三個鏈接的擴展方法我們還得到三個Func<T> S和也是三個編譯器生成的方法。

[CompilerGenerated] 
private static bool <Main>b__1(ATable m) 
{ 
    return m.Prop1; 
} 

[CompilerGenerated] 
private static bool <Main>b__2(ATable m) 
{ 
    return m.Prop2; 
} 

[CompilerGenerated] 
private static bool <Main>b__3(ATable m) 
{ 
    return m.Prop3; 
} 

現在看起來這應該是比較慢,因爲有更多的代碼。然而,由於所有的執行都被推遲到GetEnumerator()被調用,我懷疑任何明顯的差異都會出現。

可能會影響性能

  • 在鏈GetEnumerator的任何調用將導致對集合進行迭代的一些陷阱。當調用ToList時,ATable.Where().ToList().Where().ToList()將導致第一個謂詞與第二個ToList迭代。儘量讓GetEnumerator調用到最後一刻,以減少迭代次數。

LINQ實體
由於我們使用IQueryable<T>現在生成的我們的編譯器代碼是有點不同,因爲我們正在使用Expresssion<Func<T, bool>>代替我們在所有在一個正常Func<T, bool>

實施例的。
var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix" && m.Id == 10 && m.GenreType_Value == 3);

這會產生一個聲明。

IQueryable<MovieSet> allInOneWhere = Queryable.Where<MovieSet>(entityFrameworkEntities.MovieSets, Expression.Lambda<Func<MovieSet, bool>>(Expression.AndAlso(Expression.AndAlso(Expression.Equal(Expression.Property(CS$0$0000 = Expression.Parameter(typeof(MovieSet), "m"), (MethodInfo) methodof(MovieSet.get_Name)), ..tons more stuff...ParameterExpression[] { CS$0$0000 }));

最顯着的是,我們最終得到的是被解析到Expression.AndAlso件一個表達式樹。而且還喜歡期待我們只有一個呼叫Queryable.Where

var chainedWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);

我甚至懶得慣於在粘貼該編譯器代碼的方式來長。但總而言之,我們最終得到了三個電話Queryable.Where(Queryable.Where(Queryable.Where()))和三個表達式。這也是預期的,因爲我們有三個鏈接Where條款。

生成的SQL
IEnumerable<T>IQueryable<T>也不會執行,直到枚舉被調用。正因爲如此,我們可以很高興地知道,無論產生完全相同的SQL語句:

SELECT 
[Extent1].[AtStore_Id] AS [AtStore_Id], 
[Extent1].[GenreType_Value] AS [GenreType_Value], 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name] 
FROM [dbo].[MovieSet] AS [Extent1] 
WHERE (N'The Matrix' = [Extent1].[Name]) AND (10 = [Extent1].[Id]) AND (3 = [Extent1].[GenreType_Value]) 

可能影響性能的一些陷阱

  • 在鏈GetEnumerator的任何調用將導致通話出於sql,例如ATable.Where().ToList().Where()實際上將查詢與第一個謂詞匹配的所有記錄的sql,然後使用linq將列表過濾爲具有第二個謂詞的對象。
  • 由於您提到提取謂詞使用其他地方,使得確定它們的形式是Expression<Func<T, bool>>而不是簡單的Func<T, bool>。第一個可以解析爲表達式樹並轉換爲有效的sql,第二個將觸發返回所有對象,並且Func<T, bool>將在該集合上執行。

我希望這有助於回答你的問題。

+2

感謝您的深入解答! :) – Joe 2011-03-04 22:48:06

+1

+1爲一個很好的答案 – 2011-08-26 04:35:34

+1

+1謝謝你的一個真棒回答 – 2012-05-08 01:16:51