问题描述
写作有(逻辑/性能)差异:
ATable.Where(x => condition1&& condition2 &&&&&&&&&&&")3
或
ATable.Where(x => condition1).Where(x => condition2).Where(x => condition3)
我一直在使用前者,但意识到后者,我可以阅读和复制部分查询出来使用其他地方更容易。
任何想法?
长回答 很长时间,你应该在应用程式中执行更多的可读性和可维护性长
Linq To Objects
ATable。其中(x => condition1&&& condition2&&&condition3)
对于此示例由于只有一个谓词语句,编译器将只需要生成一个代理和一个编译器生成的方法。
从反射器
if (CS $<>><>> 9CachedAnonymousMethodDelegate4 == 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>
与预期的委托,因为只有一个其中
扩展方法。
ATable.Where(x => condition1).Where(x => condition2).Where(x => condition3)
现在为这个例子生成了更多的代码。
if(CS $<> 9_ChachedAnonymousMethodDelegate5 == 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>(表,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;
}
现在看起来像这样应该更慢,因为heck还有一吨多的代码。但是因为所有的执行都被推迟到 GetEnumerator()
被调用,我怀疑任何明显的差异将呈现。
- 在链中对GetEnumerator的任何调用都将导致集合被迭代。
ATable.Where()。ToList()。Where()。ToList()
会导致集合的迭代, 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)。其中(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
我甚至不打算在编译器代码中粘贴这个方法,但是简而言之,我们结束了三次调用 Queryable.Where(Queryable.Where(Queryable.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]名称] AS [名称]
从[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,在肯定中,它们的格式为
Expression< Func< T, bool>
,而不是简单地Func< T,bool>
。第一个可以解析为表达式树并转换为有效的sql,第二个将触发 ALL OBJECTS 返回并且Func
我希望这有助于回答您的问题。
Is there a (logical/performance) difference to writing:
ATable.Where(x=> condition1 && condition2 && condition3)
or
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
I've been using the former but realised that with the latter, I can read and copy parts of a query out to use somewhere else easier.Any thoughts?
Short answer
You should do what you feel is more readable and maintainable in your application as both will evaluate to the same collection.
Long answer quite long
Linq To ObjectsATable.Where(x=> condition1 && condition2 && condition3)
For this example Since there is only one predicate statement the compiler will only needs to generate one delegate and one compiler generated method.
From reflector
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>();
The compiler generated method:
[CompilerGenerated]
private static bool <Main>b__0(ATable m)
{
return ((m.Prop1 && m.Prop2) && m.Prop3);
}
As you can see there is only one call into Enumerable.Where<T>
with the delegate as expected since there was only one Where
extension method.
ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
now for this example a lot more code is generated.
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>();
Since we have three chained Extension methods we also get three Func<T>
s and also three compiler generated methods.
[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;
}
Now this looks like this should be slower since heck there is a ton more code. However since all execution is deferred until GetEnumerator()
is called I doubt any noticeable difference will present itself.
Some Gotchas that could effect performance
- Any call to GetEnumerator in the chain will cause a the collection to be iterated.
ATable.Where().ToList().Where().ToList()
will result in an iteration of the collection with the first predicate when theToList
is called and then another iteration with the secondToList
. Try to keep the GetEnumerator called to the very last moment to reduce the number of times the collection is iterated.
Linq To Entities
Since we are using IQueryable<T>
now our compiler generated code is a bit different as we are using Expresssion<Func<T, bool>>
instead of our normal Func<T, bool>
Example in all in one.var allInOneWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix" && m.Id == 10 && m.GenreType_Value == 3);
This generates one heck of a statement.
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 }));
The most notable is that we end up with one Expression tree that is parsed down to Expression.AndAlso
pieces. And also like expected we only have one call to Queryable.Where
var chainedWhere = entityFrameworkEntities.MovieSets.Where(m => m.Name == "The Matrix").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
I wont even bother pasting in the compiler code for this, way to long. But in short we end up with Three calls to Queryable.Where(Queryable.Where(Queryable.Where()))
and three expressions. This again is expected as we have three chained Where
clauses.
Generated Sql
Like IEnumerable<T>
IQueryable<T>
also does not execute until the enumerator is called. Because of this we can be happy to know that both produce the same exact sql statement:
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])
Some Gotchas that could effect performance
- Any call to GetEnumerator in the chain will cause a call out to sql, e.g.
ATable.Where().ToList().Where()
will actually query sql for all records matching the first predicate and then filter the list with linq to objects with the second predicate. - Since you mention extracting the predicates to use else where, make sure they are in the form of
Expression<Func<T, bool>>
and not simplyFunc<T, bool>
. The first can be parsed to an expression tree and converted into valid sql, the second will trigger ALL OBJECTS returned and theFunc<T, bool>
will execute on that collection.
I hope this was a bit helpful to answer your question.
这篇关于linq style,chaining where clause vs和operator的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!