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

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

   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가 상수 표현식을 올바르게 번역하는 것처럼 보이지만 변수가 포함되면 일반 비교처럼 처리합니다. 실제로 말이됩니다. 이 질문을 닫겠습니다



답변

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로 설정 해야합니다.


답변

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

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

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


답변

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) 에서 투표 하시기 바랍니다 .


답변

nullable 형식이면 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 비교 사용을 처리하려면 Object.Equals()대신==

참조를 확인


답변

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