Как я могу запросить нулевые значения в структуре сущностей?

Я хочу выполнить такой запрос

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 something = @p; @p = NULL . Кажется, что EF правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл. Я закрою этот вопрос

Обходной путь для 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, что эта ошибка также повлияла на вас.


Изменить: эта ошибка исправлена ​​в EF 4.5 ! Спасибо всем за поддержку этой ошибки!

Для обратной совместимости, он будет включен – вам нужно вручную включить параметр, чтобы сделать entry == value . Пока нет слов о том, что это за параметр. Будьте на связи!


Редактировать 2: Согласно этому сообщению командой EF, эта проблема была исправлена ​​в EF6! Woohoo!

Мы изменили поведение EF6 по умолчанию для компенсации трехзначной логики.

Это означает, что существующий код, который опирается на старое поведение ( null != null , но только при сравнении с переменной) , либо должен быть изменен, либо не будет полагаться на это поведение, либо установить UseCSharpNullComparisonBehavior в false, чтобы использовать старое нарушение поведения.

Поскольку Entity Framework 5.0 вы можете использовать следующий код для решения вашей проблемы:

 public abstract class YourContext : DbContext { public YourContext() { (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; } } 

Это должно решить ваши проблемы, поскольку Entity Framerwork будет использовать сравнение «C # like».

Существует несколько упрощенное обходное решение, которое работает с 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.

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

  1. Это может потенциально нарушить код, который уже зависит от существующего поведения.
  2. Новый перевод может повлиять на работу существующих запросов, даже если нулевой параметр используется редко.

В любом случае, будем ли мы работать над этим, будет сильно зависеть от относительного приоритета, который наши клиенты назначают ему. Если вы беспокоитесь об этой проблеме, я рекомендую вам проголосовать за нее на нашем новом сайте Feature Feature: https://data.uservoice.com .

Если это тип с нулевым значением, возможно, попробуйте использовать свойство HasValue?

 var result = from entry in table where !entry.something.HasValue select entry; 

У меня нет EF для тестирования здесь, хотя … просто предложение =)

 var result = from entry in table where entry.something.Equals(null) select entry; 

Ссылка MSDN : LINQ to SQL: .NET-интегрированный запрос для реляционных данных

для работы с Null Comparisons используйте Object.Equals() вместо ==

проверьте эту ссылку

 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 правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл.

Указывая, что все предложения 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> 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) 

Похоже, что Linq2Sql также имеет эту «проблему». Похоже, что существует истинная причина такого поведения из-за того, включены ли ANSI NULL или OFF, но это пугает разум, почему прямое «== null» действительно работает так, как вы ожидали.

Лично я предпочитаю:

 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; 

потому что он предотвращает повторение – хотя это не математически точно, но в большинстве случаев оно подходит.

Я не могу прокомментировать сообщение divega, но среди различных решений, представленных здесь, решение divega создает лучший SQL. И производительность мудрая и длина мудрый. Я только что проверил с помощью SQL Server Profiler и просмотрел план выполнения (с «SET STATISTICS PROFILE ON»).

К сожалению, в 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(); } } } } 

Следует отметить, что это грязное решение, но оно может быть реализовано очень быстро и работает для всех запросов.

Если вы предпочитаете использовать синтаксис метода (lambda), как я, вы можете сделать то же самое:

 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) query).ToTraceString(); } return result; 
 var result = from entry in table where entry.something == value||entry.something == null select entry; 

использовать это

  • Код структуры Entity Framework Сначала используйте Guid как идентификатор с другой колонкой Identity
  • Добавление подсказки при вызове функции с табличными значениями
  • Как мне сначала разделить мои таблицы в EF Code?
  • Как просмотреть SQL, созданный платформой Entity Framework?
  • Entity Framework 6 Сначала код Значение по умолчанию
  • Место для размещения Database.SetInitializer
  • Как передать параметры методу DbContext.Database.ExecuteSqlCommand?
  • Учебники ADO.NET Entity Framework
  • Группирование команд DB в Entity Framework 4.0
  • Строка соединения SQL Server Express для Entity Framework Code First
  • Ключевое слово не поддерживается: 'server'
  • Давайте будем гением компьютера.