为什么这个if / then语句返回true?

时间:2010-11-14 01:42:13

标签: c# linq

我有一个相当丑陋的服务工作,它通过遗留数据库运行并将其与我们的生产数据库进行比较:

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不包含任何元素”,但我只是做了一个!= null检查。这一定很简单,发生了什么?

8 个答案:

答案 0 :(得分:17)

如果我能教人们关于LINQ的一件事就是:查询表达式的值是表示查询的对象,而不是查询的结果 。从根本上说这是你的问题;您将查询视为其结果。查询不仅仅是餐厅是俱乐部三明治的结果。餐厅是生产俱乐部三明治的设备;查询是产生结果的设备。

答案 1 :(得分:7)

这是对你正在做的null的测试:

vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null

总是是真的;这将至少返回一个空序列......永远不会是null

您可能有意测试其长度是否大于0?

然而,有一种更简单的方法,IMO。请致电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);
  }
}
}

答案 2 :(得分:2)

因为你的查询返回了一个容器,所以它恰好是空的,返回的空检查不是返回包含的内容。

试试这个......

if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).Any())
{
  .....
}

答案 3 :(得分:1)

不要运行查询两次。它效率低下,可能会在您的代码中引入竞争条件。此外,直接使用IEnumerator<T>或使用foreach循环可以更好地支持您的逻辑。

使用:

var result = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).GetEnumerator();
if (result.MoveNext) {
    var oldDbContractItem = result.Current;
    // ...
}

foreach (var oldDbContractItem in vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number)) {
    // ...
    break;
}

答案 4 :(得分:0)

使用已接受答案中的修复程序后,您可以使用以下步骤进一步调试LINQ情况(如果需要)。 LINQ是一项令人兴奋的技术,但需要一段时间来解决它的问题 - 我想说的是一种范式转换。 Eric's answer击中头部,因为他可能帮助建造了这些东西!

LINQ的调试步骤

  1. 要处理第二个语句没有结果的问题,请将.First()更改为.FirstOrDefault()如果未找到任何结果,则会返回数据类型的默认值 - 如果数据类型为一个类,它将返回一个空值。然后,您的第二个语句也应该使用空检查而且没有错误。

  2. 然后你可以调试你的LINQ语句,找出它正在做的事情。

    • 如果使用LINQ to SQL,Visual Studio 2010中的Intellisense将显示将鼠标悬停在查询变量上时生成的SQL(不是查询结果)。如果您需要VS 2008 it's here

    • 的可视化工具
    • 类似地,如果您使用的是LINQ to Entity Framework,则可以使用visualizer plugin here获取生成的SQL。

  3. 我总是从这些工具中获取生成的SQL,将其直接粘贴到查询窗口并在那里运行。它将显示您正在返回的空集,如果这是一个问题,您可以通过这种可视化生成的语句的方式进一步调试它。

答案 5 :(得分:0)

没有元素的序列仍然是一个对象。

答案 6 :(得分:0)

Where可以返回非null值,但仍然可以解析为不包含元素的序列。在您的失败声明中,您正在使用其他内容,即Where().First()。如果来自Where的序列确实为空,那将为null。

答案 7 :(得分:0)

null与空序列(例如类似new List<T>()之类的内容)之间存在差异。您要做的是使用Any()检查序列是否包含任何元素,或者将First()替换为FirstOrDefault(),如果序列不包含任何元素,则返回null 。 (如果您使用它,请确保null不是First())可能返回的有效值。