写作是否存在(逻辑/性能)差异:
ATable.Where(x=> condition1 && condition2 && condition3)
或
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
我一直在使用前者,但意识到使用后者,我可以阅读和复制部分查询,以便更容易在其他地方使用。 有什么想法吗?
答案 0 :(得分:22)
简短回答
您应该在应用程序中执行您认为更具可读性和可维护性的内容,因为它们都将评估到同一个集合。
长答案 很长时间
Linq To Objects
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>
以及三个编译器生成的方法。
[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()
被调用,我怀疑是否会出现明显的差异。
可能会影响效果的一些问题
ATable.Where().ToList().Where().ToList()
将导致在调用ToList
时使用第一个谓词对第一个谓词进行迭代,然后使用第二个ToList
进行另一次迭代。尝试将GetEnumerator调用到最后一刻,以减少迭代集合的次数。 Linq To Entities
由于我们现在使用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])
可能会影响效果的一些问题
ATable.Where().ToList().Where()
实际上将查询sql以查找与第一个谓词匹配的所有记录,然后使用linq将列表过滤到具有第二个谓词的对象。Expression<Func<T, bool>>
,而不仅仅是Func<T, bool>
。第一个可以解析为表达式树并转换为有效的sql,第二个将触发 ALL OBJECTS 返回,Func<T, bool>
将在该集合上执行。我希望这对回答你的问题有点帮助。