Как я могу запросить нулевые значения в рамках сущности? - PullRequest
107 голосов
/ 25 марта 2009

Я хочу выполнить запрос, подобный этому

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

Ответы [ 14 ]

123 голосов
/ 30 марта 2010

Обходной путь для Linq-to-SQL:

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

Обходной путь для Linq-to-Entities (ой!):

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, чтобы использовать старое неработающее поведение.

17 голосов
/ 19 марта 2013

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

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

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

16 голосов
/ 24 ноября 2010

Существует несколько более простой обходной путь, который работает с LINQ to Entities:

var result = from entry in table
         where entry.something == value || (value == null && entry.something == null)
         select entry;

Это работает, потому что, как заметил AZ, в особых случаях LINQ to Entities x == null (т.е. сравнение на равенство с нулевой константой) и переводится в x IS NULL.

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

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

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

8 голосов
/ 26 марта 2009

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

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

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

5 голосов
/ 25 марта 2009

для сравнения нулей. Используйте Object.Equals() вместо ==

отметьте ссылку

5 голосов
/ 25 марта 2009
var result = from entry in table
             where entry.something.Equals(null)
             select entry;

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

3 голосов
/ 16 января 2015

Указывает, что все предложения Entity Framework <6.0 генерируют неуклюжий SQL. См. Второй пример «чистого» исправления. </p>

Смешной обходной путь

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

1 голос
/ 23 сентября 2011

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

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;

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

1 голос
/ 20 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...