LINQ에서 런타임 값을 갖는 .NET 클래스를 사용할 때 주의

[제목] LINQ에서 런타임 값을 갖는 .NET 클래스를 사용할 때 주의

LINQ TO Entitiy 혹은 LINQ TO SQL에서 조건식에 변수값을 사용하는 것과 .NET 클래스를 직접 사용하는 것은 경우에 따라 다른 결과를 초래할 수 있다. 이는 Framework이 런타임시 그 값이 결정되는 .NET 클래스를 (런타임 값이 아닌) SQL의 내장함수로 직접 변환할 수 있기 때문이다.

이 아티클은 Facebook의 [C# 스터디 그룹]에서 최근에 논의된 내용에 대한 Summary이다. 예제 코드는 Entity Framework(EF)와 LINQ Query를 사용하였는데, 문제는 예제A는 잘 작동하는 반면 예제B는 결과를 리턴하지 않는다는 것이었다.

만약 개발자 로컬 머신에서 이 코드를 실행한다면, 동일한 결과를 냈을 것이다. 하지만, Azure와 같은 분산 환경에 실행된다면, 원하는 결과를 얻지 못할 가능성이 있다.

MyDbContext db = new MyDbContext();

// 예제A. 날짜형 변수와 비교
DateTime now = DateTime.Now;
var data1 = from t in db.Tests
            where t.CreateDate.Year == now.Year
            && t.CreateDate.Month == now.Month
            && t.CreateDate.Day == now.Day
            select t;

foreach (Test t in data1)
{
    Console.WriteLine("{0} {1}", t.Id, t.CreateDate);
}

// 예제B. DateTime.Now 를 비교식에 직접 대입
var data2 = from t in db.Tests
        where t.CreateDate.Year == DateTime.Now.Year
        && t.CreateDate.Month == DateTime.Now.Month
        && t.CreateDate.Day == DateTime.Now.Day
        select t;

foreach (Test t in data2)
{
    Console.WriteLine("{0} {1}", t.Id, t.CreateDate);
}

Entity Framework과 LINQ TO SQL과 같은 ORM Framework에서 이러한 문제가 발생하면 SQL Profiler와 같은 툴을 사용해 SQL 서버로 들어가는 쿼리를 먼저 살펴보는 것이 좋다.

위의 예제A가 실행되면 Entity Framework은 아래와 같은 쿼리를 생성한다.

날짜 Day 부분만 살펴보면, 아래와 같이 sp_executesql 를 호출하여 @p__linq__2 라는 파라미터를 사용하고 그 파라미터 값을 고정된 값(2014-09-29 11:43:31)으로 지정하고 있음을 알 수 있다. 즉 C# 변수 now의 값이 SQL문에서 리터럴 값으로 사용되는 것이다.

exec sp_executesql N'SELECT ...((DATEPART (day, [Extent1].[CreateDate])) = (DATEPART (day, @p__linq__2)))
@p__linq__2='2014-09-29 11:43:31.6223019'

반면 예제B가 실행되면 EF는 다음과 같은 쿼리를 생성한다.

다시 날짜 Day 부분만 살펴보면, 여기서는 SQL 내장함수인 SysDateTime()을 직접 대입하고 있음을 볼 수 있다. 즉, C#의 DateTime.Now.Day을 사용하면 Entity Framework은 SQL 함수 SysDateTime()을 사용하도록 변환하고 있는 것이다.

((DATEPART (day, [Extent1].[CreateDate])) = (DATEPART (day, SysDateTime())))

이러한 차이는 LINQ를 사용하는 머신과 DB 서버가 물리적으로 분산되어 있을 때 (시간대가 다를 때) 혹은 DB 서버가 다른 Time Zone을 사용할 때, 다른 결과를 리턴하게 할 수 있다. 또한 위의 경우는 Day를 비교한 것이지만 만약 Hour를 비교한다면 이러한 문제는 더 명확히 발생할 수 있다.

따라서, LINQ 사용시 많은 경우 동적인 값을 표시하는 .NET 클래스를 직접 대입하기 보다는 이를 변수에 지정하고 그 변수를 이용하는 것이 더 안전한 방법이 될 수 있다. 물론 .NET 클래스를 직접 대입해야 하는 경우도 있을 수 있겠지만...



본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.