#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
.