C# 为什么这个if/then语句返回true?
我有一个相当难看的服务工作,它运行在一个遗留数据库中,并将其与我们的生产数据库进行比较:C# 为什么这个if/then语句返回true?,c#,linq,C#,Linq,我有一个相当难看的服务工作,它运行在一个遗留数据库中,并将其与我们的生产数据库进行比较: if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null) { var oldDbContractItem = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Ite
if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null) {
var oldDbContractItem = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).First();
// check to see if there were changes
if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp)
{
oldDbContractItem.Update(vendorContractItem);
}
}
我将在
var oldDbContratItem
上得到一个错误,“Sequence contains no elements”,但我刚刚做了一个!=空检查。这必须很简单,到底发生了什么?使用已接受答案中的修复后,您可以使用以下步骤进一步调试LINQ情况(如果需要)。LINQ是一项令人兴奋的技术,但需要一段时间才能让人清醒过来——我认为这是一种范式转变。一针见血,因为他可能帮助建造了这些东西
LINQ的调试步骤
.First()
更改为.FirstOrDefault()
如果未找到任何内容,它将返回数据类型的默认值-如果数据类型是类,它将返回空值。然后,您的第二条语句也应该使用空检查,并且没有错误- 如果使用LINQ to SQL,Visual Studio 2010中的Intellisense将显示当您将鼠标悬停在查询变量(而不是查询结果)上时生成的SQL。如果您需要VS 2008的可视化工具
- 类似地,如果使用LINQtoEntity框架,则可以使用
这是针对您正在执行的
null
的测试:
vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null
这永远是真的;将始终返回至少一个空序列。。。绝不允许null
var oldDbContractItem = vendorContract.Item
.Where(x => x.ItemNumber == contractItem.Item_Number).FirstOrDefault();
if(oldDbContractItem != null) //would be null if there are no items
{
// check to see if there were changes
if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp)
{
oldDbContractItem.Update(vendorContractItem);
}
}
}
您可能想测试其长度是否大于0
不过,在我看来,有一种更简单的方法,调用FirstOrDefault()
而不是First()
,并完全忽略预测试。然后,测试FirstOrDefault()
的结果是否为null
var oldDbContractItem = vendorContract.Item
.Where(x => x.ItemNumber == contractItem.Item_Number).FirstOrDefault();
if(oldDbContractItem != null) //would be null if there are no items
{
// check to see if there were changes
if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp)
{
oldDbContractItem.Update(vendorContractItem);
}
}
}
没有元素的序列仍然是一个对象。因为您的查询返回了一个容器,它恰好是空的,空检查是在返回上,而不是返回所包含的内容 试试这个
if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).Any())
{
.....
}
其中
可以返回非空值,但仍然解析为不包含元素的序列。在失败语句中,您使用的是其他内容,即Where().First()
。如果中的序列确实为空,则该值为空。空序列与空序列之间存在差异(例如类似于新列表()
)。您要做的是使用Any()
检查序列是否包含任何元素,或者将First()
替换为FirstOrDefault()
,如果序列不包含元素,则返回null
。(如果使用该选项,请确保null
不是可由First()
返回的有效值)。不要运行两次查询。它效率低下,可能会在代码中引入竞争条件。另外,通过直接使用IEnumerator
或使用foreach
循环,可以更好地支持您的逻辑
使用以下任一选项:
var result = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).GetEnumerator();
if (result.MoveNext) {
var oldDbContractItem = result.Current;
// ...
}
或
如果我能教人们关于LINQ的一件事,那就是:查询表达式的值是表示查询的对象,而不是查询结果。从根本上说,这是你的问题;您将查询视为其结果。一个问题并不是一个结果,就像一家餐厅是一个俱乐部三明治。餐厅是一种生产俱乐部三明治的设备;查询是一种产生结果的设备。这实际上并不能解决任何问题。问题是他对空序列的测试是错误的。您只是在掩盖错误。@Yuliy:公认的答案还建议将FirstOrDefault
作为调试的一个步骤。这不是掩盖任何东西,而是使过程更易于管理……但是如果您注意到我更关注第二条代码语句而不是第一条,而不是真理测试,那么您是对的。我将把这些工具超链接留在这里,以防它们对调试LINQ情况的人有用。请注意,在您的特定情况下,@Andrew Barber有一个更优雅的解决方案(因为您想要第一个元素,如果它存在的话),我的解决方案只是测试序列中包含的内容。+1这将是一种方法,例如,您将在一个结果序列中处理(可能)多个项目,而不是仅处理一个项目。@Andrew:foreach
如果您处理的是可能的多个项目,则应该这样做。第二次运行查询只是为了调用Any
是愚蠢的(或者更糟糕的是,如果查询是针对共享数据存储的,它会引入竞争条件)。我更喜欢这个答案,因为从基本原理的角度来看,+1注意到原始代码不必要地运行了两次查询。