linq 样式、链接 where 子句与 and 运算符
写入是否存在(逻辑/性能)差异:
ATable.Where(x=> condition1 && condition2 && condition3)
或
ATable.Where(x=> ;condition1).Where(x=>condition2).Where(x=>condition3)
我一直在使用前者,但意识到使用后者,我可以读取查询的部分内容并将其复制到在其他地方使用更容易。 有什么想法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简短回答
您应该在应用程序中执行您认为更具可读性和可维护性的操作,因为两者都将评估为同一集合。
长答案 相当长
Linq To Objects
ATable.Where(x=>条件1 &&条件2 &&条件3)
对于此示例,由于只有一个谓词语句,因此编译器只需生成一个委托和一个编译器生成的方法。
来自反射器
编译器生成的方法:
如您所见,只有一次对
Enumerable.Where
的调用,如预期的那样,因为只有一个Where
扩展方法。ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
现在,对于此示例,生成了更多代码。由于我们有三个链式扩展方法,因此我们还获得了三个 Func 以及三个编译器生成的方法。
现在看起来应该会更慢,因为代码多了很多。然而,由于所有执行都被推迟到调用 GetEnumerator() 为止,我怀疑是否会出现任何明显的差异。
一些可能影响性能的问题
ATable.Where().ToList().Where().ToList()
将导致在调用ToList
时使用第一个谓词对集合进行迭代,然后使用第二个ToList
进行另一次迭代。尝试将 GetEnumerator 调用到最后一刻,以减少迭代集合的次数。Linq To 实体
由于我们使用的是
IQueryable
,现在我们的编译器生成的代码有点不同,因为我们使用Expression>
而不是正常的 < code>Func示例集于一身。
var allInOneWhere =EntityFrameworkEntities.MovieSets.Where(m => m.Name == "黑客帝国" && m.Id == 10 && m.GenreType_Value == 3);
这会生成一个非常糟糕的声明。
IQueryable; allInOneWhere = Queryable.Where(entityFrameworkEntities.MovieSets, Expression.Lambda>(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 == "黑客帝国").Where(m => m.Id == 10).Where(m => m.GenreType_Value == 3);
我什至不会为此粘贴编译器代码,太长了,但总而言之,我们最终会调用 Queryable.Where(Queryable.Where(Queryable.Where())) 。和三个表达式。这也是预期的,因为我们有三个链接的
Where
子句。生成的Sql
与
IEnumerable
一样,IQueryable
在调用枚举器之前也不会执行。因此,我们很高兴知道两者都会生成完全相同的 sql 语句:一些可能影响性能的陷阱
ATable.Where().ToList().Where()
实际上会在 sql 中查询与第一个谓词匹配的所有记录,然后使用第二个谓词使用 linq to object 过滤列表。Expression>
的形式,而不仅仅是Func
Func< ;T,布尔>
。第一个可以解析为表达式树并转换为有效的 sql,第二个将触发返回所有对象,并且Func
将在该集合上执行。我希望这对回答您的问题有所帮助。
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 Objects
ATable.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
The compiler generated method:
As you can see there is only one call into
Enumerable.Where<T>
with the delegate as expected since there was only oneWhere
extension method.ATable.Where(x=>condition1).Where(x=>condition2).Where(x=>condition3)
now for this example a lot more code is generated.Since we have three chained Extension methods we also get three
Func<T>
s and also three compiler generated methods.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
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 usingExpresssion<Func<T, bool>>
instead of our normalFunc<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 toQueryable.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 chainedWhere
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:Some Gotchas that could effect performance
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.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.