엔터티 프레임 워크에서 null 값을 쿼리하려면 어떻게해야합니까?


109

다음과 같은 쿼리를 실행하고 싶습니다.

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

그리고 얻을 IS NULL생성.

편집 됨 : 처음 두 개의 답변 후에 Linq to SQL이 아닌 Entity Framework를 사용하고 있음을 명확히해야한다고 느낍니다. 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가 상수 표현식을 올바르게 번역하는 것처럼 보이지만 변수가 포함되면 일반 비교처럼 처리합니다. 실제로 말이됩니다. 이 질문을 닫겠습니다


17
나는 그것이 정말로 말이되지 않는다고 생각한다 ... 커넥터는 약간 똑똑해야하고 우리에게 일을하도록 요구하지 않아야한다. 올바른 C # 쿼리의 SQL에서 올바른 번역을 수행하라. 이로 인해 예기치 않은 동작이 발생합니다.
Julien N

6
나는 줄리앙 함께 있으니,이 EF의 부품에 대한 고장입니다
미스터 벨

1
이것은 표준의 실패이며, null과의 비교로 인해 ANSI NULL이 영구적으로 설정된 SQL Server 2016에서 영구적으로 정의되지 않은 결과가 발생하기 때문에 점점 더 악화되고 있습니다. Null은 알 수없는 값을 나타낼 수 있지만 "null"자체는 알 수없는 값이 아닙니다. null 값과 null 값을 비교하면 절대적으로 참이되지만, 불행히도 표준은 부울 논리뿐만 아니라 상식에서 벗어납니다.
Triynko 2015 년

답변:


126

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에서 수정되었습니다! 우후!

3 값 논리를 보완하기 위해 EF6의 기본 동작을 변경했습니다.

즉, 이전 동작 ( null != null변수와 비교할 때만) 에 의존하는 기존 코드를 해당 동작에 의존하지 않도록 변경하거나 UseCSharpNullComparisonBehavior이전 중단 된 동작을 사용하려면 false로 설정 해야합니다.


6
버그 보고서에 투표했습니다. 바라건대 그들은 이것을 고쳤습니다. 이 버그가 vs2010 베타에 존재하는 것을 정말 기억한다고 말할 수 없습니다 ...
noobish

2
어서 마이크로 소프트 ... 정말?!?!? 버전 4.1에서?!?! +1
David

1
Linq-To-SQL 해결 방법이 작동하지 않는 것 같습니다 (Guid로 시도 하시겠습니까?). 엔터티 사용-해결 방법은 L2S에서 작동하지만 끔찍한 SQL을 생성합니다. 코드에서 if 문을 작성해야했습니다(var result = from ...; if(value.HasValue) result = result.Where(e => e.something == value) else result = result.Where(e => e.something == null);
Michael Stum

5
Object.Equals는 실제로 작동합니다(where Object.Equals(entry.something,value))
Michael Stum

5
@ leen3o (또는 다른 사람)-EF 4.5 / 5.0에서이 문제가 해결 된 곳을 아직 찾은 사람이 있습니까? 5.0을 사용하고 있는데 여전히 오작동합니다.
Shaul Behr

17

Entity Framework 5.0부터 문제를 해결하기 위해 다음 코드를 사용할 수 있습니다.

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

Entity Framerwork는 'C # like'null 비교를 사용하므로 문제가 해결됩니다.


16

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 (즉, null 상수에 대한 같음 비교)이고 x IS NULL로 변환되기 때문에 작동합니다.

우리는 현재이 동작을 변경하여 동등성의 양쪽이 모두 nullable 인 경우 보상 비교를 자동으로 도입하는 것을 고려하고 있습니다. 하지만 몇 가지 문제가 있습니다.

  1. 이로 인해 이미 기존 동작에 의존하는 코드가 손상 될 수 있습니다.
  2. 새로운 변환은 null 매개 변수가 거의 사용되지 않는 경우에도 기존 쿼리의 성능에 영향을 미칠 수 있습니다.

어쨌든 우리가이 작업을 수행할지 여부는 고객이 할당 한 상대적 우선 순위에 따라 크게 달라집니다. 이 문제에 관심이 있으시면 새로운 기능 제안 사이트 ( https://data.uservoice.com) 에서 투표 하시기 바랍니다 .


9

nullable 형식이면 HasValue 속성을 사용해보십시오.

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

여기에서 테스트 할 EF가 없습니다 ... 그냥 제안 =)


1
음 ... 이것은 null을 찾는 경우에만 작동하지만 == null어쨌든 사용하면 버그가 발생하지 않습니다. 요점은 값이 널일 수있는 변수 값으로 필터링하고 널 값이 널 레코드를 찾도록하는 것입니다.
Dave Cousineau

1
당신의 대답이 나를 구했습니다. 내 엔터티 모델 클래스에서 nullable 형식을 사용하는 것을 잊었고 (x => x.Column == null)작동 하지 못했습니다 . :)
Reuel Ribeiro

이것은 System.NullReferenceException allready 객체가 null이기 때문에을 제공합니다 !
TiyebM


5

Null 비교 사용을 처리하려면 Object.Equals()대신==

참조를 확인


이것은 Linq-To-Sql에서 완벽하게 작동하며 적절한 SQL도 생성합니다 (여기에있는 일부 다른 답변은 끔찍한 SQL 또는 잘못된 결과를 생성합니다).
Michael Stum

내가 null, 와 비교하고 싶다고 가정 해보자. Object.Equals(null)만약 그 Object자체가 null이면 어떨까?
TiyebM

4

모든 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
    );

결과는 다음과 같습니다.

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)

SQL에서 실행되는 코드는 더 깨끗하고 빠르지 만 EF는 SQL 서버로 보내기 전에 모든 조합에 대해 새 쿼리 계획을 생성하고 캐시하므로 다른 해결 방법보다 느립니다.
Burak Tamtürk

2
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

Linq2Sql에도이 "문제"가있는 것으로 보입니다. ANSI NULL이 ON 또는 OFF인지 여부로 인해이 동작에 대한 유효한 이유가있는 것으로 보이지만 직선 "== null"이 실제로 예상대로 작동하는 이유는 당황합니다.


1

개인적으로 선호합니다 :

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;

반복을 방지하기 때문입니다. 수학적으로 정확하지는 않지만 대부분의 경우에 적합합니다.


0

divega의 게시물에 대해 설명 할 수는 없지만 여기에 제시된 여러 솔루션 중에서 divega의 솔루션이 최고의 SQL을 생성합니다. 성능 측면과 길이 측면 모두. 방금 SQL Server Profiler를 사용하여 실행 계획을 확인했습니다 ( "SET STATISTICS PROFILE ON"사용).


0

불행히도 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();
            }
        }
    }
}

이는 더러운 해결 방법이지만 매우 빠르게 구현할 수 있고 모든 쿼리에 대해 작동하는 해결 방법이라는 점에 유의해야합니다.


경고가 명확하지 않은 경우 향후 버전의 SQL Server에서 ANSI NULLS가 영구적으로 ON으로 설정되면 즉시 작동이 중지됩니다.
Triynko

0

메서드 (람다) 구문을 사용하는 것을 선호한다면 다음과 같이 똑같이 할 수 있습니다.

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;

-1
var result = from entry in table    
             where entry.something == value||entry.something == null                   
              select entry;

그것을 사용하십시오


5
값을 요청하더라도 값이 일치하는 모든 항목과 무언가가 null 인 모든 항목을 선택하므로 매우 잘못되었습니다.
Michael Stum
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.