.Any() с нулевыми значениями — Возвращает False, когда я ожидаю True

#c# #linq #extension-methods #nullable

#c# #linq #методы расширения #nullable

Вопрос:

У меня есть модель представления с обнуляемым значением int…

 public ObjectViewModel (){
    public int? Total
}
  

… и в моей базе данных есть несколько строк, где общее значение равно нулю.

Несмотря на это, это всегда возвращает false:

 bool exists = repo.AllRows() // renamed this for clarity; returns IQueryable
                  .Any(r => r.Total == vm.Total); // I know r.Total and vm.Total
                                                  // are both null
  

Но следующее возвращает true (как и ожидалось):

 bool exists = repo.All().Any(r => r.Total == null);
  

Есть идеи, что я здесь делаю не так?

Комментарии:

1. если vm имеет значение null, вы получите исключение NullReferenceException.

2. Откуда вы получаете виртуальную машину и какое значение она имеет? Исходя из результатов, я бы ожидал, что оно имеет значение, которого нет в вашем наборе результатов.

3. Какова цель вызова All перед Any ?

4. что касается виртуальной машины, даже если вы не указали, что делаете это в цикле, будьте осторожны, если вы это делаете: blogs.msdn.com/b/ericlippert/archive/2009/11/12 /…

5. В чем предикат All ? Насколько я знаю, его нельзя вызвать без него.

Ответ №1:

Предполагая, что вы имели в виду «vm.Total равен null», и это All() была опечатка…

Я думаю, ваша проблема в том, как это переводится в SQL:

  • первый запрос переводится как предложение WHERE с r.Total = @param1
  • второй запрос переводится как предложение WHERE с использованием IS NULL

В MSDN есть хорошее описание для NULL:

Значение NULL указывает на то, что значение неизвестно. Значение NULL отличается от пустого или нулевого значения. Никакие два нулевых значения не равны. Сравнения между двумя нулевыми значениями или между нулевым и любым другим значением возвращают unknown, потому что значение каждого NULL неизвестно.

Это означает, что вы не можете использовать операторы сравнения в SQL — и, следовательно, также не можете в Linq to sql.

Есть несколько способов обойти это:

  • чтобы проверить наличие null в vm.Total, прежде чем выбирать, какую форму запроса использовать.
  • чтобы использовать Object.Equals для сравнения — смотрите Этот пост в блоге для получения дополнительной информации об этом — http://www.brentlamborn.com/post/LINQ-to-SQL-Null-check-in-Where-Clause.aspx

Комментарии:

1. 1, это, безусловно, пример несоответствия в C # и SQL. Я полагаю, вы можете использовать оператор объединения null (??) в C #, чтобы LINQ использовал ISNULL(foo, 0) вместо этого. Что касается All() вызова, он не только не нужен ему, но и не будет компилироваться. Я подозреваю, что это была ошибка.

2. Добавлена ссылка на сообщение в блоге — brentlamborn.com/post /…

3. Путаница .All() возникает из-за того, что у меня в репозитории есть метод All(), который возвращает результат from m in context select m . Я не имею в виду метод расширения .All(). Он компилируется просто отлично. 🙂

4. @ScottSEA не кажется ли это плохой идеей?

5. @Josh — да, рефакторинг, пока мы говорим. <вздох> 🙂

Ответ №2:

Ответ Джоша кажется мне наиболее точным. Просто используйте оператор объединения null :

 bool exists = repo.AllRows().Any(r => r.Total ?? 0 == vm.Total ?? 0);
  

… и у вас больше не будет типа «ГДЕ NULL = NULL», а «ГДЕ 0 = 0», что нормально.

Ответ №3:

Как говорит Bala R, если vm имеет значение null, то вы не сможете получить доступ к Total свойству, и оно должно вызвать исключение NullReferenceException .

Ваш запрос должен быть:

 bool exists = repo.Any(r => r.Total == null);
  

exists будет true, если есть запись, по крайней мере, со свойством null in Total .

Ответ №4:

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

 Any(r => r.Total == vm==null ? null : vm.Total)
  

Комментарии:

1. Я понимаю, о чем вы говорите, но разве вам не нужны какие-то круглые скобки там? Any(r => r.Total == (vm==null) ? null: vm.Total)

2. На самом деле я не запускал код, но я полагаю, что вы этого не делаете. Найдите троичный оператор или условный оператор, как C # любит его называть. msdn.microsoft.com/en-us/library/ty67wk28 (v = VS.100).aspx

Ответ №5:

Any Метод возвращает true, если какой-либо из элементов в вашей коллекции соответствует условию, указанному в лямбда-выражении. Таким образом, ни один из элементов в repo не имеет Total, который равен vm.Total , однако есть элементы, которые равны null, поэтому второй возвращает true.

Для проверки вставил туда немного отладочного кода,

 Console.WriteLine("vm.Total="   vm.Total.ToString());
foreach (var r in repo)
    Console.WriteLine("r.Total=" r.Total == null ? "null" : r.Total.ToString());
  

И взгляните на элементы, вы не должны видеть, r.Total который равен vm.Total , и вы увидите по крайней мере один null .