简答
您应该在您的应用程序中执行您认为更具可读性和可维护性的操作,因为两者都将评估为同一个集合。
长答案 很长
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);
}
如您所见,由于只有一个 Where 扩展方法,因此只有一次调用 Enumerable.Where<T> 与预期的委托一样。
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 的任何调用都会导致集合被迭代。
ATable.Where().ToList().Where().ToList() 将导致在调用 ToList 时使用第一个谓词对集合进行迭代,然后使用第二个 ToList 进行另一次迭代。尽量将 GetEnumerator 调用保持在最后一刻,以减少迭代集合的次数。
Linq To 实体
由于我们使用的是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 将列表过滤为具有第二个谓词的对象。
- 既然您提到提取谓词以在 else where 中使用,请确保 确保它们采用
Expression<Func<T, bool>> 而不仅仅是 Func<T, bool> 的形式。第一个可以解析为表达式树并转换为有效的 sql,第二个将触发 ALL OBJECTS 返回,Func<T, bool> 将在该集合上执行。
我希望这对回答你的问题有点帮助。