2010-02-21 43 views
4

在这样的码:为什么普通法则在评估布尔表达式时不适合LINQ?

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

其中insuranceNumberSearch为空时,剩余的表达不为空,而在下面的代码:

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

不管表达的所有部分被评估insuranceNumberSearch的是null,或者不是空值。

我使用LINQ to NHibernate的

UPDATE:

不幸的是我已经把第一个片段是错误的。正确的是:

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()将导致引用对象为空异常。可悲的是,LINQ(或者LINQ到NHibernate)不服从这样一个好的行为,并且即使在insuranceNumberSearchnull并且导致错误时也评估所有表达式。

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

+0

我觉得条件是相同的。也许还有一些地方你可能会寻找错误。你能指定你期望得到什么结果吗?你会得到什么结果? – 2010-02-21 15:27:31

+0

我从来没有尝试Linq2NHiberate,它是否编译IQueryable与表达查询? – 2010-02-21 15:31:40

+0

@Tomas:据我所知,他的“所有部分......被评估”(原文如此)为“没有短路”,这意味着对于'insuranceNumberSearch == null',您最终会在'ei.InsuranceNumber处得到NRE .contains(insuranceNumberSearch.Trim())' – 2010-02-21 15:48:24

回答

5

打我,但问心无愧就不会使用

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

在你的说法,无论是在LINQ表达或不?

+0

我的猜测是,如果NHibernate不支持快捷操作符,这个表达式将会有同样的问题,并且海报试图通过使用条件表达式来强制使用预期的快捷方式。 – BlueMonkMN 2010-02-21 16:09:38

+0

尽管我不得不承认,我很无知如何翻译这两个表达式的变体 - 而且我希望更好的支持正常/理智的方法,使用上面的纯OR。尽管对于你的想法和样本,你确实得到了我的+1。 :) – 2010-02-21 16:16:05

+0

PLZ看到我的更新。 – 2010-02-22 06:09:27

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 NHibernate的不执行shotcut条件操作的LINQ方式对象呢。

的程序的输出是:

Checking if One contains O 
One 
null 

记住LINQ是表示用于转换成其他语法任意表达式的方法。据我了解,LINQ本身不会评估表达式,NHibernate会(不管那是什么)。所以LINQ只是将你提供的表达式转换成与NHibernate兼容的表达式。如果NHibernate的不具有较快捷的条件操作的手段,我能想象的3件事情之一发生:

  1. NHibernate的将评估表达自己的方式(就像LINQ to SQL中永远的快捷方式,即使操作你使用VB.NET中的非快捷AND运算符)。
  2. 你会得到一个错误,表达式不能用NHibernate语法表示。
  3. 只有一部分查询将被转换为NHibernate语法;其余的将由LINQ to Objects评估。
+0

但我不能使用System.String以外的任何其他类型 – 2010-02-22 06:10:19

+0

我并不是建议您以不同的方式编写代码(解决问题),只是试图解释它为什么会如此工作(回答问题)。 – BlueMonkMN 2010-02-22 11:35:46

+0

我对解决方案的建议与您链接到的其他问题的解决方案类似:将集合拆分成保险项NumberSearch == null和其他项目不为空的项目,并且仅在第二个集合中使用LINQ到NHibernate。那可能吗? – BlueMonkMN 2010-02-22 11:44:54

2

看来问题出在LINQ的NHibernate提供程序上 - 对于LINQ来说,对象(只对查询执行简单的句法转换来进行方法调用),这个法则会按照预期保持。问题是,当与表达式树一起工作时,提供者可以对您的代码进行任何修改。

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

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

无论如何,如果短路导致运营商对问题的供应商,你可能需要将查询拆分为两个:

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

请参阅我对有关您的问题的评论:如果您尝试对'insuranceNumberSearch == null'使用'insuranceNumberSearch.Trim()',结果不一样。 – 2010-02-21 16:35:43

+0

@Tomas,你的解决方案将工作,但我真正使用这个查询需要多个参数(insuranceNumberSearch)。什么时候我有5个参数?然后,我必须根据5个参数中的每一个的空值创建32个查询!事实上,我可以使用HQL,但我开始使用NHibernate-to-LINQ,因为它的简单性。 – 2010-02-22 06:13:46