LINQ,其中具有OR子句lambda表达式和空值返回的结果不完整条款 [英] LINQ where clause with lambda expression having OR clauses and null values returning incomplete results

查看:1689
本文介绍了LINQ,其中具有OR子句lambda表达式和空值返回的结果不完整条款的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

总之

我们已经Where子句,它没有返回预期的结果中使用的lambda表达式。

we have a lambda expression used in the Where clause, which is not returning the "expected" result.

快速摘要

在analysisObjectRepository对象,有特定的对象也包含在属性命名父父关系。我们查询这个analysisObjectRepository返回某些对象。

in the analysisObjectRepository object, there are certain objects which also contain the parent relationship in a property named Parent. we are querying this analysisObjectRepository to return some objects.

详细信息

什么下面的代码应该做的是,返回根,第一个子女(直系子)和包含ID值特定对象的孙子。

what the code below supposed to do is, returning the root, the first children (immediate children) and grandchildren of a specific object containing the ID value.

在下面的代码,共同。某种意义上说,所有的结果,这使得任何3个独立的OR条件真应该返回作为结果的

in the code below, common sense says that all the results which makes any of the 3 seperate OR conditions true should be returned as in the results.

List<AnalysisObject> analysisObjects = 
    analysisObjectRepository
        .FindAll()
        .Where(x => x.ID               == packageId ||
                    x.Parent.ID        == packageId || 
                    x.Parent.Parent.ID == packageId)
        .ToList();



但上面的代码只返回子孙,而不是返回根对象(用空父值),这使

but the above code only returns the children and grandchildren, while not returning the root objects (with a null Parent value) which make the

x.ID == packageId

条件为真。

仅这使得第二个

x.Parent.ID == packageId

和第三

x.Parent.Parent.ID == packageId

子句返回

如果我们只写代码与下面的代码返回根对象,则返回,所以我们完全确信analysisObjectRepository包含的所有对象。

If we only write the code to return the root object with the below code, it is returned, so we are totally sure that analysisObjectRepository contains all the objects

List<AnalysisObject> analysisObjects = 
    analysisObjectRepository
        .FindAll()
        .Where(x => x.ID == packageId )
        .ToList();



然而,当我们把它改写为代表,我们得到了预期的结果,返回所有预期目标

However, when we rewrite it as a delegate, we get the expected result, returning all the expected objects.

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表达式的东西吗?这是一个非常简单的3部分或条件,我们认为这使得任何三个条件属实的任何对象应返回。我们怀疑有根对象提供一个null父值可能会导致问题,但无法弄清楚究竟。

Are we missing something in the lambda expression? it is a really simple 3 part OR condition and we think that any object that makes any of the three conditions true should be returned. we suspected that the root object having a null Parent value might cause a problem but couldn't figure it out exactly.

任何帮助将是巨大的。

推荐答案

你的第二个委托不是匿名委托的第一个(而不是拉姆达)格式的重写。

Your second delegate is not a rewrite of the first in anonymous delegate (rather than lambda) format. Look at your conditions.

First:

第一= ||包标识x.Parent.ID == ||包标识x.Parent.Parent.ID ==包标识

x.ID == packageId || x.Parent.ID == packageId || x.Parent.Parent.ID == packageId



二:

Second:

(x.ID == packageId) || (x.Parent != null && x.Parent.ID == packageId) || 
(x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)

要在lambda的调用将抛出一个异常,任何 X ,其中ID不匹配,无论是父母为空或不匹配和祖父母为空。空检查复制到拉姆达,它应该正常工作。

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.

如果您原来的对象不是列表< T> ,那么我们有没有办法知道什么的FindAll()的返回类型是,这是否不实施的IQueryable 接口。如果确实如此,那么很可能解释的差异。因为lambda表达式可以在编译时被转换成表达式来; Func键< T>> 但匿名委托不能的,那么你可能会使用实施的的IQueryable 使用匿名委托版本时使用的lambda版本,但LINQ到对象时。

If your original object is not a List<T>, then we have no way of knowing what the return type of FindAll() is, and whether or not this implements the IQueryable interface. If it does, then that likely explains the discrepancy. Because lambdas can be converted at compile time into an Expression<Func<T>> but anonymous delegates cannot, then you may be using the implementation of IQueryable when using the lambda version but LINQ-to-Objects when using the anonymous delegate version.

这会也解释了为什么你的拉姆达不造成一个的NullReferenceException 。如果你到lambda表达式传递给一些农具的IEnumerable< T> 不过的的IQueryable< T> ,拉姆达的运行评估(这是没有其他方法不同,匿名或不)会抛出一个的NullReferenceException 第一次遇到一个对象,其中< 。code> ID 不等于目标和父母或祖父母为空

This would also explain why your lambda is not causing a NullReferenceException. If you were to pass that lambda expression to something that implements IEnumerable<T> but not IQueryable<T>, runtime evaluation of the lambda (which is no different from other methods, anonymous or not) would throw a NullReferenceException the first time it encountered an object where ID was not equal to the target and the parent or grandparent was null.

考虑下面简单的例子:

IQueryable<MyObject> source = ...; // some object that implements IQueryable<MyObject>

var anonymousMethod =  source.Where(delegate(MyObject o) { return o.Name == "Adam"; });    
var expressionLambda = source.Where(o => o.Name == "Adam");



这两种方式产生完全不同的结果。

These two methods produce entirely different results.

第一个查询是一个简单的版本。在一个委托匿名方法的结果是然后传递到的IEnumerable<为MyObject>。凡扩展方法,其中源的全部内容将(使用普通编译的代码在内存中手动)对您的委托进行检查。换句话说,如果你熟悉C#迭代器块,它的东西,像这样:

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 of source 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:

public IEnumerable<MyObject> MyWhere(IEnumerable<MyObject> dataSource, Func<MyObject, bool> predicate)
{
    foreach(MyObject item in dataSource)
    {
        if(predicate(item)) yield return item;
    }
}



在这里最关键的一点是,你实际上执行您筛选的内存的客户端上。例如,如果你的来源是一些SQL ORM,就在查询中没有 WHERE 条款;整个结果集将被带回给客户端过滤的没有

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.

第二个查询,它使用一个lambda表达式,转换为一个表达式来; Func键<为MyObject,布尔>> ,并使用的IQueryable<为MyObject>。凡()扩展方法。这将导致也类型为的IQueryable℃的对象;为MyObject> 。所有这一切的工作原理是那么传递的表达式的底层供应商。 这就是为什么你没有得到一个的NullReferenceException 。这是完全由查询提供如何将表达式转换(其中,而不是实际的编译功能,它可以直接打电话,是的的表示逻辑使用对象表达的)到一些它可以使用。

The second query, which uses a lambda expression, is converted to an Expression<Func<MyObject, bool>> and uses the IQueryable<MyObject>.Where() extension method. This results in an object that is also typed as IQueryable<MyObject>. All of this works by then passing the expression to the underlying provider. This is why you aren't getting a NullReferenceException. 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.

这是简单的方法来看到的区别(或者,至少,有的)的区分,将是把一个您的来电其中,中拉姆达版本之前打电话给 AsEnumerable()。这将迫使你的代码使用LINQ到对象(这意味着它运行在的IEnumerable< T> 像匿名委托版本,而不是的IQueryable< T> ; 像拉姆达目前版本一样),并预期你会得到异常

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 to Where in the lambda version. This will force your code to use LINQ-to-Objects (meaning it operates on IEnumerable<T> like the anonymous delegate version, not IQueryable<T> like the lambda version currently does), and you'll get the exceptions as expected.

长期和短期的它是你的lambda表达式被翻译成某种对数据源的查询,而匿名方法的版本正在评估的全部在存储器中的数据源。无论是做您的lambda的转化为一个查询是不是代表了你期待的逻辑,这就是为什么它不产生你期望的结果。

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.

这篇关于LINQ,其中具有OR子句lambda表达式和空值返回的结果不完整条款的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆