LINQ возвращает 0 результатов, если использует переменную nullable int, точные результаты при использовании "null"

У меня есть таблица под названием "test", которая имеет только 1 столбец "NullableInt" (тип NULL int)

Записи: 1, 2, null

int? nullableInt = null;
var t = db.tests.Where(x => x.NullableInt == null).ToList(); // returns 1 record
var t2 = db.tests.Where(x => x.NullableInt == nullableInt).ToList(); // returns 0 records

По какой-то причине t2 возвращает 0 записей, даже если использует переменную "nullableInt", которая имеет значение null, точно так же, как t, которое сравнивается с "null"

Любая помощь будет принята с благодарностью!

Ответ 1

Запросы могут быть построены таким образом:

var q = db.tests;
if(nullableInt.HasValue)
{
   q = q.Where(x => x.NullableInt == nullableInt.Value);
}
else
{
   q = q.Where(x => x.NullableInt == null);
}
var t2 = q.ToList();

Ответ 2

Yep - это ошибка в LINQ-to-SQL/Entity Framework. IS NULL запросы будут генерироваться только в том случае, если вы задаете нулевой запрос в запросе вместо переменной, которая в настоящее время является нулевой.

Второй запрос будет генерировать

SELECT .......
WHERE NullableInt == @someParam
WHERE @someParam is null.

Если первый генерирует соответствующий IS NULL в предложении WHERE.

Если вы используете LINQ-to-SQL, вы можете записывать свои запросы в Console.Out, чтобы убедиться сами, и если вы используете EF, то ToTraceString() должен показывать вам ту же информацию (или SQL Server профилировщик)

Ответ 3

TL;DR

Если вы используете DbContext в EF6, это исправлено.

Если вы используете EF5 (или ObjectContext в EF6), вам необходимо установить ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior в true. Для этого в DbContext используйте это:

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;

.

Подробнее

Коренной причиной этой проблемы является различие в том, как база данных сравнивает нулевые значения и как С# сравнивает нулевые значения. Поскольку вы пишете свой запрос в С#, вы хотите использовать семантику С#.

В EF5 мы ввели ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior, который позволил вам выбрать семантику С# вместо семантики базы данных. Значение по умолчанию - false (так что существующие запросы не волшебным образом начинают возвращать разные результаты при обновлении до EF5). Но вы можете установить его в true, и ваши запросы возвратят строки.

Если вы используете DbContext в EF5, вам нужно опуститься до ObjectContext, чтобы установить его:

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;

Если вы используете EF6, тогда он уже установлен на true в DbContext, поэтому вам хорошо идти. Мы решили, что это вызывает столько путаницы, что стоило потенциально повлиять на существующие запросы.

Ответ 4

Существует еще одно решение, которое всегда будет работать, хотя и с небольшим предостережением:

int? nullableInt = null;
var t2 = db.tests.Where(x => object.Equals(x.NullableInt, nullableInt)).ToList();

Когда значение равно null, вы получите правильный IS NULL запрос, однако, когда его значение не равно null, вы получите что-то вроде:

SELECT ...
WHERE ([t0].[NullableInt] IS NOT NULL) AND ([t0].[NullableInt] = @p0) 

Очевидно, что у него есть условие extra (источник которого вызывает недоумение). При этом оптимизатор запросов SQL Server должен обнаружить это, поскольку @p0 является ненулевым значением, первое условие является надмножеством и сократит предложение where.

Ответ 5

Делал:

var t2 = db.tests.Where(x => x.NullableInt == nullableInt ?? null).ToList(); 

Работа?

Похоже, это безумие.