Почему обычные законы в оценке логического выражения не вписываются в LINQ? - PullRequest
4 голосов
/ 21 февраля 2010

В таком коде:

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 равен нулю или не равен нулю.

Я использую LINQ для 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();

В обоих случаях, когда insuranceNumberSearch равен null, остальные выражения больше не оцениваются. Если такого поведения не существует, insuranceNumberSearch.Trim() приведет к тому, что эталонный объект будет равен нулю исключение. К сожалению, LINQ (или, возможно, LINQ-to-NHibernate) не подчиняется такому хорошему поведению и оценивает все выражения, даже если insuranceNumberSearch равно null и приводит к ошибке.

ОБНОВЛЕНИЕ 2: Я нашел похожий вопрос: The || (или) Оператор в Linq с C #

Ответы [ 3 ]

5 голосов
/ 21 февраля 2010

Побей меня, но ты бы не использовал

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

в вашем утверждении, в выражении LINQ или нет?

3 голосов
/ 21 февраля 2010

Как показывает этот код, это не проблема 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 не выполняет условные операции shotcut, как это делает LINQ to Objects.

Вывод программы:

Checking if One contains O
One
null

Имейте в виду, что LINQ - это способ представления произвольных выражений для преобразования в другие синтаксисы. Насколько я понимаю, само LINQ не будет оценивать выражение, а NHibernate (что бы это ни было). Поэтому LINQ просто преобразует предоставленное вами выражение в выражение, совместимое с NHibernate. Если в NHibernate нет средств для представления условных операций с ярлыками, я могу представить одну из трех происходящих вещей:

  1. NHibernate оценит выражение по-своему (точно так же, как LINQ to SQL всегда будет выполнять операции быстрого вызова AND, даже если вы используете не-быстрый оператор AND из VB.NET).
  2. Вы получите ошибку, что выражение не может быть представлено в синтаксисе NHibernate.
  3. Только ограниченная часть запроса будет преобразована в синтаксис NHibernate; остальное будет оценено LINQ to Objects.
2 голосов
/ 21 февраля 2010

Кажется, что проблема в поставщике NHibernate для LINQ - для LINQ для объектов (который выполняет простое синтаксическое преобразование запросов в вызовы методов), закон будет соответствовать ожидаемому. Проблема в том, что при работе с деревьями выражений провайдер может вносить любые изменения в ваш код.

Что может быть еще хуже - целевая среда выполнения может не поддерживать точную семантику некоторых операций C #. Например, он может не иметь такой же реализации арифметики с плавающей запятой.

В вашем примере кажется, что NHibernate не поддерживает поведение короткого замыкания. Мне не понятно, почему это может быть проблемой - он может вычислять вторую часть выражения, но результат должен быть таким же.

В любом случае, если операторы короткого замыкания вызывают проблемы у поставщика, вам, вероятно, придется разделить запрос на две части:

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