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

Я хочу выполнить запрос вроде этого

   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 для тестирования здесь, хотя... просто предложение =)

Ответ 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;

используйте