为什么评估布尔表达式的普通定律不适合LINQ?

时间:2010-02-21 15:15:37

标签: c# linq linq-to-nhibernate

在这样的代码中:

if (insuranceNumberSearch == null 
     ? true  
     : ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim())) 
   doSomething();

其中insuranceNumberSearch为null,其余表达式在以下代码中不为空:

var q = from ei in session.Linq<EmployeeInsurance>()
        where insuranceNumberSearch == null 
                ? true 
                : ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim())
        select ei;

无论insuranceNumberSearch为null还是非null,都会评估表达式的所有部分。

我正在使用LINQ to NHibernate

更新

不幸的是我把第一个片段弄错了。正确的是:

if (insuranceNumberSearch == null || (insuranceNumberSearch != null && ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim()))
doSomething();

bool b1 = insuranceNumberSearch == null ? true : ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim());
if (b1)
doSomething();

上述两个insuranceNumberSearchnull时,不再评估剩余的表达式。如果此类行为不存在,insuranceNumberSearch.Trim()将导致引用对象为空异常。遗憾的是,即使insuranceNumberSearchnull并且导致错误,LINQ(或者LINQ-to-NHibernate)也不会遵循这样的好行为并评估所有表达式。

更新2:我发现了一个类似的问题:The || (or) Operator in Linq with C#

3 个答案:

答案 0 :(得分:5)

打败我,但你不会使用

if (
     (insuranceNumberSearch == null) ||
     ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim()))
  doSomething();

在你的陈述中,是否在LINQ表达式中?

答案 1 :(得分:3)

如此代码所示,这不是LINQ的问题。此代码与您的代码类似,但它不会在LINQ表达式中评估条件的两端:

class Program
{
  class MyClass
  {
     public string value;
     public MyClass(string value) { this.value = value; }
     public bool Contains(char elem)
     {
        Console.WriteLine("Checking if {0} contains {1}", value, elem);
        return value.Contains(elem);
     }
  }

  static void Main(string[] args)
  {
     var mc = new MyClass[2];
     mc[0] = new MyClass("One");
     mc[1] = new MyClass(null);
     var q = from i in mc where i.value == null ? true : i.Contains('O') select i;
     foreach (MyClass c in q)
        Console.WriteLine(c.value == null ? "null" : c.value);
  }
}

LINQ to NHibernate的表达式求值程序可能不像LINQ to Objects那样执行shotcut条件操作。

该计划的输出是:

Checking if One contains O
One
null

请记住,LINQ是一种表示任意表达式转换为其他语法的方法。据我了解,LINQ本身不会评估表达式,NHibernate会(不管是什么)。因此,LINQ只是将您提供的表达式转换为与NHibernate兼容的表达式。如果NHibernate没有表示快捷条件操作的方法,我可以想象出现的三件事之一:

  1. NHibernate将以自己的方式评估表达式(就像LINQ to SQL将始终快捷方式AND操作,即使您使用VB.NET中的非快捷方式AND运算符)。
  2. 您将收到一个错误,表达式无法用NHibernate语法表示。
  3. 只有有限部分的查询会转换为NHibernate语法;其余的将由LINQ to Objects评估。

答案 2 :(得分:2)

似乎问题出在LINQ的NHibernate提供程序中 - 对于LINQ to对象(它只是简单的查询到方法调用的语法转换),法律将按预期保持。问题是,当使用表达式树时,提供程序可以对您的代码进行任何修改。

更糟糕的是 - 目标执行环境可能不支持某些C#操作的确切语义。例如,它可能没有相同的浮点算术实现。

在您的示例中,似乎NHibernate不支持短路行为。我不清楚为什么这会是一个问题 - 它可以评估表达式的第二部分,但结果应该是相同的。

无论如何,如果短路运营商给提供商带来问题,您可能需要将查询分成两部分:

var q = 
  insuranceNumberSearch == null 
    ? session.Linq<EmployeeInsurance>() 
    : (from ei in session.Linq<EmployeeInsurance>() 
       where ei.InsuranceNumber.Contains(insuranceNumberSearch.Trim()) 
       select ei);