4


1

ブール式の評価における通常の法則がLINQに適合しないのはなぜですか?

そのようなコードでは:

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

「insuranceNumberSearch」がnullである場合、次のコードでは残りの式はnullではありません。

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

式のすべてのセクションは、insuranceNumberSearchがヌルであるかヌルでないかに関係なく評価されます。

私はLINQ to NHibernateを使用しています

更新:

残念ながら、最初のスニペットは間違っています。 正しいです:

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

or

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

上記の両方で、「insuranceNumberSearch」が「null」の場合、残りの式は評価されなくなります。 そのような動作が存在しない場合、 `insuranceNumberSearch.Trim()`は_referenceオブジェクトはnull_例外となります。 残念ながら、LINQ(またはLINQ-to-NHibernate)は、このような素晴らしい動作に従わず、「insuranceNumberSearch」が「null」でエラーになった場合でも、すべての式を評価します。

*更新2:*同様の質問を見つけました:https://stackoverflow.com/questions/772261/the-or-operator-in-linq-with-c[The || (または)C#を使用したLinqの演算子]

3 Answer


5


私を打つが、あなたは使用しないだろう

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

あなたの声明で、それはLINQ式であるかどうか?


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のようにショットカット条件付き操作を実行しない可能性があります。

プログラムの出力は次のとおりです。

Checking if One contains O
One
null

LINQは、他の構文に変換するための任意の式を表す方法であることに注意してください。 私が理解しているように、LINQ自体は式を評価せず、NHibernateは(それが何であれ)評価します。 したがって、LINQは、指定した式をNHibernateと互換性のある式に変換するだけです。 NHibernateにショートカット条件付き操作を表す手段がない場合、次の3つのことが発生することを想像できます。

  1. NHibernateは式を独自の方法で評価します(LINQと同様に VB.NETから非ショートカットAND演算子を使用した場合でも、SQLは常にAND演算をショートカットします。

  2. 式を表現できないというエラーが表示されます NHibernate構文。

  3. クエリの限られた部分のみがNHibernateに変換されます 構文;残りはLINQ to Objectsによって評価されます。


2


問題は、LINQのNHibernateプロバイダーにあるようです-オブジェクトへのLINQ(クエリからメソッド呼び出しへの単純な構文変換を行う)の場合、法は予想どおりに保持されます。 問題は、_expression trees_を操作するときに、プロバイダーがコードを変更できることです。

さらに悪いことに、ターゲット実行環境は、一部のC#操作の正確なセマンティクスをサポートしていない場合があります。 たとえば、浮動小数点演算の実装が同じでない場合があります。

あなたの例では、NHibernateは短絡動作をサポートしていないようです。 なぜそれが問題になるのかは明確ではありません。式の2番目の部分を評価できますが、結果は同じでなければなりません。

とにかく、短絡演算子がプロバイダーに問題を引き起こす場合、おそらくクエリを2つに分割する必要があります。

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