[.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 인 경우 보상 비교를 자동으로 도입하는 것을 고려하고 있습니다. 하지만 몇 가지 문제가 있습니다.
- 이로 인해 이미 기존 동작에 의존하는 코드가 손상 될 수 있습니다.
- 새로운 변환은 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)