Cih*_*urt 13 c# lambda delegates expression
问题简而言之
我们有一个在Where子句中使用的lambda表达式,它不会返回"预期"结果.
快速摘要
在analyzeObjectRepository对象中,某些对象也包含名为Parent的属性中的父关系.我们正在查询此analyzeObjectRepository以返回一些对象.
详情
下面的代码应该做的是,返回包含ID值的特定对象的根,第一个子节点(直接子节点)和孙子节点.
在下面的代码中,常识表示所有使3个单独的OR条件中的任何一个成为真的结果应该在结果中返回.
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId ||
x.Parent.ID == packageId ||
x.Parent.Parent.ID == packageId)
.ToList();
Run Code Online (Sandbox Code Playgroud)
但上面的代码只返回子孙,而不返回根对象(使用null父值)
x.ID == packageId
Run Code Online (Sandbox Code Playgroud)
条件是真的.
只有制作第二个的对象
x.Parent.ID == packageId
Run Code Online (Sandbox Code Playgroud)
第三
x.Parent.Parent.ID == packageId
Run Code Online (Sandbox Code Playgroud)
条款被退回.
如果我们只编写代码以使用下面的代码返回根对象,则返回它,因此我们完全确定analyzeObjectRepository包含所有对象
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId )
.ToList();
Run Code Online (Sandbox Code Playgroud)
但是,当我们将其重写为委托时,我们得到预期的结果,返回所有预期的对象.
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();
Run Code Online (Sandbox Code Playgroud)
题
我们在lambda表达式中遗漏了什么吗?它是一个非常简单的3部分OR条件,我们认为应该返回任何使三个条件中的任何一个成为真的对象.我们怀疑具有null Parent值的根对象可能会导致问题,但无法准确找出问题.
任何帮助都会很棒.
Ada*_*son 13
你的第二个代表不是重写匿名委托(而不是lambda)格式的第一个委托.看看你的情况.
第一:
x.ID == packageId || x.Parent.ID == packageId || x.Parent.Parent.ID == packageId
Run Code Online (Sandbox Code Playgroud)
第二:
(x.ID == packageId) || (x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
Run Code Online (Sandbox Code Playgroud)
对lambda的调用将为xID不匹配的任何内容抛出异常,并且父级为null或不匹配且祖父级为null.将空检查复制到lambda中,它应该可以正常工作.
如果你的原始对象不是a List<T>,那么我们无法知道返回类型FindAll()是什么,以及它是否实现了IQueryable接口.如果确实如此,则可能解释了这种差异.因为lambdas可以在编译时转换为Expression<Func<T>> 但匿名委托不能,所以你可能在使用IQueryablelambda版本时使用实现,但在使用匿名委托版本时使用LINQ-to-Objects.
这也可以解释为什么你的lambda没有导致NullReferenceException.如果你要的是lambda表达式传递的东西实现IEnumerable<T>,但不是 IQueryable<T>,拉姆达的运行评估(这是没有其它方法不同,匿名或不)会抛出一个NullReferenceException在第一次遇到一个对象,其中ID不等于目标而父母或祖父母是空的.
考虑以下简单示例:
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");
Run Code Online (Sandbox Code Playgroud)
这两种方法产生完全不同的结果.
第一个查询是简单版本.匿名方法导致委托然后传递给IEnumerable<MyObject>.Where扩展方法,其中source将针对您的委托检查整个内容(使用普通编译代码手动在内存中).换句话说,如果你熟悉C#中的迭代器块,它就像这样:
public IEnumerable<MyObject> MyWhere(IEnumerable<MyObject> dataSource, Func<MyObject, bool> predicate)
{
foreach(MyObject item in dataSource)
{
if(predicate(item)) yield return item;
}
}
Run Code Online (Sandbox Code Playgroud)
这里的重点是你实际上是在客户端进行内存过滤.例如,如果您的源是某个SQL ORM,则WHERE查询中不会有任何子句; 整个结果集将被带回客户端并在那里进行过滤.
使用lambda表达式的第二个查询将转换为a Expression<Func<MyObject, bool>>并使用IQueryable<MyObject>.Where()扩展方法.这会导致一个对象也被输入为IQueryable<MyObject>.所有这些都适用于将表达式传递给基础提供程序.这就是为什么你没有得到一个NullReferenceException.这完全取决于查询提供程序如何将表达式(它可以只是调用的实际编译函数,它是使用对象的表达式逻辑的表示)转换为它可以使用的东西.
一种看待区别(或者至少存在区别)的简单方法是AsEnumerable()在调用Wherelambda版本之前调用.这将强制您的代码使用LINQ-to-Objects(意味着它IEnumerable<T>像匿名委托版本一样运行,而不是IQueryable<T>像lambda版本当前那样),并且您将获得预期的异常.
它的长短是你的lambda表达式被转换成针对你的数据源的某种查询,而匿名方法版本正在评估内存中的整个数据源.无论做什么,将lambda转换为查询都不能代表您期望的逻辑,这就是为什么它不会产生您期望的结果.
尝试使用与委托相同的条件写入lambda.像这样:
List<AnalysisObject> analysisObjects =
analysisObjectRepository.FindAll().Where(
(x =>
(x.ID == packageId)
|| (x.Parent != null && x.Parent.ID == packageId)
|| (x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
).ToList();
Run Code Online (Sandbox Code Playgroud)