带有 lambda 表达式的 LINQ where 子句,该表达式具有 OR 子句和返回不完整结果的 null 值
问题简而言之
我们在Where子句中使用了一个lambda表达式,它没有返回“预期”结果。
快速摘要
在analysisObjectRepository 对象中,某些对象在名为Parent 的属性中也包含父关系。我们正在查询这个analysisObjectRepository以返回一些对象。
detail
下面的代码应该做的是,返回包含 ID 值的特定对象的根、第一个子对象(直接子对象)和孙子对象。
在下面的代码中,常识表明,使 3 个单独的 OR 条件中的任何一个为 true 的所有结果都应该像结果中一样返回。
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId ||
x.Parent.ID == packageId ||
x.Parent.Parent.ID == packageId)
.ToList();
但上面的代码只返回子对象和孙子对象,而不返回使条件成立的根对象(具有 null Parent 值)
x.ID == packageId
。
构成第二个
x.Parent.ID == packageId
和第三个
x.Parent.Parent.ID == packageId
仅返回
子句的对象。如果我们只用下面的代码编写返回根对象的代码,那么它就会被返回,所以我们完全确定analysisObjectRepository包含所有对象
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId )
.ToList();
但是,当我们将其重写为委托时,我们得到了预期的结果,返回了所有预期的对象。
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(delegate(AnalysisObject x)
{
return
(x.ID == packageId) ||
(x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null &&
x.Parent.Parent != null &&
x.Parent.Parent.ID == packageId); })
.ToList();
问题
我们是否在 lambda 表达式中遗漏了某些内容?这是一个非常简单的三部分 OR 条件,我们认为任何使三个条件中的任何一个为 true 的对象都应该被返回。我们怀疑具有 null Parent 值的根对象可能会导致问题,但无法准确地弄清楚。
任何帮助都会很棒。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的第二个委托不是以匿名委托(而不是 lambda)格式重写第一个委托。看你的条件了。
第一:
第二:
对于 ID 不匹配且父级为 null 或不匹配且祖父级为 null 的任何
x
,对 lambda 的调用将引发异常。将 null 检查复制到 lambda 中,它应该可以正常工作。在问题评论后编辑
如果您的原始对象不是
List
,那么我们无法知道FindAll()
的返回类型是什么,以及是否或者没有实现 IQueryable 接口。如果确实如此,那么这可能可以解释这种差异。由于 lambda 可以在编译时转换为Expression>
但匿名委托不能,因此您可能正在使用IQueryable< 的实现/code> 使用 lambda 版本时,但使用匿名委托版本时使用 LINQ-to-Objects。
这也可以解释为什么您的 lambda 不会导致
NullReferenceException
。如果您要将 lambda 表达式传递给实现IEnumerable
但不IQueryable
的对象,则 lambda 的运行时评估 (这与其他方法(无论是否匿名)没有什么不同)在第一次遇到ID
不等于目标和父级或祖级的对象时会抛出NullReferenceException
为空。添加于 2011 年 3 月 16 日上午 8:29 EDT
考虑以下简单示例:
这两种方法产生完全不同的结果。
第一个查询是简单版本。匿名方法生成一个委托,然后将其传递给
IEnumerable.Where
扩展方法,其中将检查source
的全部内容(使用普通方法在内存中手动检查)已编译的代码)针对您的委托。换句话说,如果您熟悉 C# 中的迭代器块,则类似于执行以下操作:这里的重点是您实际上是在客户端内存中执行过滤。例如,如果您的源是某些 SQL ORM,则查询中不会有
WHERE
子句;整个结果集将被带回客户端并在那里进行过滤。第二个查询使用 lambda 表达式,转换为
Expression>
并使用IQueryable.Where()
扩展方法。这会产生一个类型也为IQueryable
的对象。所有这些都是通过将表达式传递给底层提供者来实现的。 这就是您没有收到NullReferenceException
的原因。这完全取决于查询提供者如何将表达式(它不是一个可以调用的实际编译函数,而是使用对象的表达式的逻辑表示)转换为它想要的东西。可以使用。查看区别(或者至少是存在区别)的一个简单方法是在调用
AsEnumerable()
之前调用AsEnumerable()
>Where 在 lambda 版本中。这将强制您的代码使用 LINQ-to-Objects(这意味着它像匿名委托版本一样在IEnumerable
上运行,而不是像 lambda 版本那样在IQueryable
上运行目前是这样),您将得到预期的异常。TL;DR 版本
总而言之,您的 lambda 表达式正在被转换为针对数据源的某种查询,而匿名方法版本正在评估内存中的整个数据源。无论将 lambda 转换为查询,所做的任何操作都不代表您期望的逻辑,这就是为什么它不会产生您期望的结果。
Your second delegate is not a rewrite of the first in anonymous delegate (rather than lambda) format. Look at your conditions.
First:
Second:
The call to the lambda would throw an exception for any
x
where the ID doesn't match and either the parent is null or doesn't match and the grandparent is null. Copy the null checks into the lambda and it should work correctly.Edit after Comment to Question
If your original object is not a
List<T>
, then we have no way of knowing what the return type ofFindAll()
is, and whether or not this implements theIQueryable
interface. If it does, then that likely explains the discrepancy. Because lambdas can be converted at compile time into anExpression<Func<T>>
but anonymous delegates cannot, then you may be using the implementation ofIQueryable
when using the lambda version but LINQ-to-Objects when using the anonymous delegate version.This would also explain why your lambda is not causing a
NullReferenceException
. If you were to pass that lambda expression to something that implementsIEnumerable<T>
but notIQueryable<T>
, runtime evaluation of the lambda (which is no different from other methods, anonymous or not) would throw aNullReferenceException
the first time it encountered an object whereID
was not equal to the target and the parent or grandparent was null.Added 3/16/2011 8:29AM EDT
Consider the following simple example:
These two methods produce entirely different results.
The first query is the simple version. The anonymous method results in a delegate that's then passed to the
IEnumerable<MyObject>.Where
extension method, where the entire contents ofsource
will be checked (manually in memory using ordinary compiled code) against your delegate. In other words, if you're familiar with iterator blocks in C#, it's something like doing this:The salient point here is that you're actually performing your filtering in memory on the client side. For example, if your source were some SQL ORM, there would be no
WHERE
clause in the query; the entire result set would be brought back to the client and filtered there.The second query, which uses a lambda expression, is converted to an
Expression<Func<MyObject, bool>>
and uses theIQueryable<MyObject>.Where()
extension method. This results in an object that is also typed asIQueryable<MyObject>
. All of this works by then passing the expression to the underlying provider. This is why you aren't getting aNullReferenceException
. It's entirely up to the query provider how to translate the expression (which, rather than being an actual compiled function that it can just call, is a representation of the logic of the expression using objects) into something it can use.An easy way to see the distinction (or, at least, that there is) a distinction, would be to put a call to
AsEnumerable()
before your call toWhere
in the lambda version. This will force your code to use LINQ-to-Objects (meaning it operates onIEnumerable<T>
like the anonymous delegate version, notIQueryable<T>
like the lambda version currently does), and you'll get the exceptions as expected.TL;DR Version
The long and the short of it is that your lambda expression is being translated into some kind of query against your data source, whereas the anonymous method version is evaluating the entire data source in memory. Whatever is doing the translating of your lambda into a query is not representing the logic that you're expecting, which is why it isn't producing the results you're expecting.
尝试使用与委托相同的条件编写 lambda。像这样:
Try writting the lambda with the same conditions as the delegate. like this:
您正在检查委托中的
Parent
属性是否为 null。同样的情况也适用于 lambda 表达式。You are checking
Parent
properties for null in your delegate. The same should work with lambda expressions too.