Я хочу выполнить запрос вроде этого
var result = from entry in table
where entry.something == null
select entry;
и получить IS NULL
.
Отредактировано:
После первых двух ответов я чувствую необходимость уточнить, что я использую Entity Framework
а не Linq to SQL. Метод object.Equals(), похоже, не работает в EF.
Изменить №2:
Вышеприведенный запрос работает по назначению. Он корректно генерирует IS NULL
. Однако мой производственный код был
value = null;
var result = from entry in table
where entry.something == value
select entry;
а сгенерированный SQL был something = @p; @p = NULL
. Кажется, что EF правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл. Я закрою этот вопрос
Ответ 1
Обходной путь для Linq-to-SQL:
var result = from entry in table
where entry.something.Equals(value)
select entry;
Временное решение для Linq-to-Entities (ouch!):
var result = from entry in table
where (value == null ? entry.something == null : entry.something == value)
select entry;
Это неприятная ошибка, которая несколько раз укусила меня. Если эта ошибка также повлияла на вас, посетите отчет об ошибке в UserVoice и сообщите Microsoft, что эта ошибка также повлияла на вас.забастовкa >
Изменить: Эта ошибка исправлена в EF 4.5! Спасибо всем за поддержку этой ошибки!
Для обратной совместимости он будет включен - вам нужно вручную включить настройку, чтобы сделать работу entry == value
. Пока нет слов о том, что это за параметр. Оставайтесь с нами!
Изменить 2: Согласно этой должности командой EF, эта проблема была исправлена в EF6! Woohoo!
Мы изменили поведение EF6 по умолчанию для компенсации трехзначной логики.
Это означает, что существующий код, который опирается на старое поведение (null != null
, но только при сравнении с переменной), либо нужно будет изменить, чтобы не полагаться на это поведение, либо установить UseCSharpNullComparisonBehavior
в false, чтобы использовать старое нарушение поведения.
Ответ 2
Существует несколько упрощенное обходное решение, которое работает с LINQ to Entities:
var result = from entry in table
where entry.something == value || (value == null && entry.something == null)
select entry;
Это работает, потому что, как заметил А.З., особый случай LINQ to Entities x == null (т.е. сравнение равенства с нулевой константой) и переводит его в x IS NULL.
В настоящее время мы рассматриваем возможность изменения этого поведения, чтобы автоматически вводить компенсирующие сравнения, если обе стороны равенства являются нулевыми. Однако есть несколько проблем:
- Это может привести к повреждению кода, который уже зависит от существующего поведения.
- Новый перевод может повлиять на производительность существующих запросов, даже если нулевой параметр используется редко.
В любом случае, будем ли мы работать над этим, это будет сильно зависеть от относительного приоритета, который наши клиенты назначают ему. Если вы беспокоитесь об этой проблеме, я рекомендую вам проголосовать за нее на нашем новом сайте Feature Feature: https://data.uservoice.com.
Ответ 3
Поскольку Entity Framework 5.0 вы можете использовать следующий код для решения вашей проблемы:
public abstract class YourContext : DbContext
{
public YourContext()
{
(this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
}
}
Это должно решить ваши проблемы, поскольку Entity Framerwork будет использовать "С# like" null-сравнение.
Ответ 4
Если это тип с нулевым значением, возможно, попробуйте использовать свойство HasValue?
var result = from entry in table
where !entry.something.HasValue
select entry;
У меня нет EF для тестирования здесь, хотя... просто предложение =)
Ответ 5
var result = from entry in table
where entry.something.Equals(null)
select entry;
Ссылка MSDN: LINQ to SQL:.NET-интегрированный запрос для реляционных данных
Ответ 6
чтобы иметь дело с Null Comparisons, используйте Object.Equals()
вместо ==
проверьте ссылка
Ответ 7
var result = from entry in table
where entry.something == null
select entry;
Вышеуказанный запрос работает по назначению. Он правильно генерирует IS NULL. Однако мой производственный код был
var value = null;
var result = from entry in table
where entry.something == value
select entry;
а сгенерированный SQL был чем-то = @p; @p = NULL. Кажется, что EF правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл.
Ответ 8
Указывая, что вся инфраструктура Entity Framework < 6.0 создают некоторый неудобный SQL. См. Второй пример для "чистого" исправления.
Смешное обходное решение
// comparing against this...
Foo item = ...
return DataModel.Foos.FirstOrDefault(o =>
o.ProductID == item.ProductID
// ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
&& item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null
&& item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null
&& item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null
&& o.Width == w
&& o.Height == h
);
приводит к SQL как:
SELECT TOP (1) [Extent1].[ID] AS [ID],
[Extent1].[Name] AS [Name],
[Extent1].[DisplayName] AS [DisplayName],
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductStyleID] AS [ProductStyleID],
[Extent1].[MountingID] AS [MountingID],
[Extent1].[Width] AS [Width],
[Extent1].[Height] AS [Height],
[Extent1].[FrameID] AS [FrameID],
FROM [dbo].[Foos] AS [Extent1]
WHERE (CASE
WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */)
AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN
CASE
WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit)
WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit)
END
WHEN (([Extent1].[ProductStyleID] IS NULL)
AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN
CASE
WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit)
WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit)
END
WHEN (([Extent1].[MountingID] IS NULL)
AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN
CASE
WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit)
WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit)
END
WHEN (([Extent1].[FrameID] IS NULL)
AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit)
WHEN (NOT (([Extent1].[FrameID] IS NULL)
AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit)
END) = 1
Нехорошее обходное решение
Если вы хотите сгенерировать более чистый SQL, что-то вроде:
// outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame;
if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID;
else filterProductStyle = o => o.ProductStyleID == null;
if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID;
else filterMounting = o => o.MountingID == null;
if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID;
else filterFrame = o => o.FrameID == null;
return DataModel.Foos.Where(o =>
o.ProductID == item.ProductID
&& o.Width == w
&& o.Height == h
)
// continue the outrageous workaround for proper sql
.Where(filterProductStyle)
.Where(filterMounting)
.Where(filterFrame)
.FirstOrDefault()
;
приводит к тому, что вы хотели в первую очередь:
SELECT TOP (1) [Extent1].[ID] AS [ID],
[Extent1].[Name] AS [Name],
[Extent1].[DisplayName] AS [DisplayName],
[Extent1].[ProductID] AS [ProductID],
[Extent1].[ProductStyleID] AS [ProductStyleID],
[Extent1].[MountingID] AS [MountingID],
[Extent1].[Width] AS [Width],
[Extent1].[Height] AS [Height],
[Extent1].[FrameID] AS [FrameID],
FROM [dbo].[Foos] AS [Extent1]
WHERE ([Extent1].[ProductID] = 1 /* @p__linq__0 */)
AND ([Extent1].[Width] = 16 /* @p__linq__1 */)
AND ([Extent1].[Height] = 20 /* @p__linq__2 */)
AND ([Extent1].[ProductStyleID] IS NULL)
AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */)
AND ([Extent1].[FrameID] IS NULL)
Ответ 9
Похоже, что Linq2Sql также имеет эту "проблему". Похоже, что существует истинная причина такого поведения из-за того, включены ли ANSI NULL или OFF, но это пугает разум, почему прямой "== null" фактически работает так, как вы ожидали.
Ответ 10
Лично я предпочитаю:
var result = from entry in table
where (entry.something??0)==(value??0)
select entry;
над
var result = from entry in table
where (value == null ? entry.something == null : entry.something == value)
select entry;
потому что он предотвращает повторение - хотя это не является математически точным, но он хорошо подходит для большинства случаев.
Ответ 11
Я не могу комментировать divega post, но среди различных решений, представленных здесь, решение divega создает лучший SQL. И производительность мудрая и длина мудрый. Я просто проверил с помощью SQL Server Profiler и просмотрел план выполнения (с "SET STATISTICS PROFILE ON" ).
Ответ 12
К сожалению, в Entity Framework 5 DbContext проблема все еще не исправлена.
Я использовал это обходное решение (работает с MSSQL 2012, но параметр ANSI NULLS может быть устаревшим в любой будущей версии MSSQL).
public class Context : DbContext
{
public Context()
: base("name=Context")
{
this.Database.Connection.StateChange += Connection_StateChange;
}
void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
{
// Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework
// that is not fixed in EF 5 when using DbContext.
if (e.CurrentState == System.Data.ConnectionState.Open)
{
var connection = (System.Data.Common.DbConnection)sender;
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "SET ANSI_NULLS OFF";
cmd.ExecuteNonQuery();
}
}
}
}
Следует отметить, что это грязное обходное решение, но оно может быть реализовано очень быстро и работает для всех запросов.
Ответ 13
Если вы предпочитаете использовать синтаксис метода (лямбда), как я, вы можете сделать то же самое:
var result = new TableName();
using(var db = new EFObjectContext)
{
var query = db.TableName;
query = value1 == null
? query.Where(tbl => tbl.entry1 == null)
: query.Where(tbl => tbl.entry1 == value1);
query = value2 == null
? query.Where(tbl => tbl.entry2 == null)
: query.Where(tbl => tbl.entry2 == value2);
result = query
.Select(tbl => tbl)
.FirstOrDefault();
// Inspect the value of the trace variable below to see the sql generated by EF
var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString();
}
return result;
Ответ 14
var result = from entry in table
where entry.something == value||entry.something == null
select entry;
используйте