LINQ..?

LINQ(Language-Integrated Query)는 C# 언어에 직접 쿼리 기능을 통합하는 방식을 기반으로 하는 기술 집합 이름이다.

쿼리는 데이터 소스에서 데이터를 검색하는 식입니다. 쿼리는 일반적으로 특수화된 쿼리 언어로 표현된다.

관계형 데이터베이스에는 SQL이 사용되고 XML에는 XQuery가 사용되는 것처럼 시간에 따라 다양한 형식의 데이터 소스에 대해 서로 다른 언어가 개발되었다.

해당 데이터 베이스에 맞는 쿼리구문을 배워야 했지만 LINQ는 다양한 데이터 소스 및 형식에 사용할 수 있는 일관된 모델을 제공함으로써 이러한 상황을 단순화한다.

관련 지식 : IEnumerable VS IQueryable

LINQ특징

  • 쿼리 식은 LINQ 사용 데이터 소스에서 데이터를 쿼리하고 변환하는 데 사용할 수 있습니다
  • 쿼리 식은 익숙한 C# 언어 구문을 많이 사용하기 때문에 쉽게 익힐 수 있습니다.
  • 대부분의 경우 컴파일러가 형식을 유추할 수 있기 때문에 명시적으로 형식을 제공할 필요는 없지만 쿼리 식의 변수는 모두 강력한 형식을 갖습니다.
  • 쿼리는 쿼리 변수를 반복할 때까지(예: foreach 문) 실행되지 않습니다.
  • 컴파일 타임에 쿼리 식은 C# 사양에 명시된 규칙에 따라 표준 쿼리 연산자 메서드 호출으로 변환됩니다.
  • 일반적으로 LINQ 쿼리를 작성하는 경우 가능하면 쿼리 구문을 사용하고 필요한 경우 메서드 구문을 사용하는 것이 좋습니다.
  • Count 또는 Max와 같은 일부 쿼리 작업은 해당하는 쿼리 식 절이 없으므로 메서드 호출로 표현해야 합니다.
  • 쿼리 식은 쿼리가 적용되는 형식에 따라 식 트리 또는 대리자로 컴파일될 수 있습니다. IEnumerable 쿼리는 대리자로 컴파일됩니다. IQueryable 및 IQueryable 쿼리는 식 트리로 컴파일됩니다(다른 포스팅에서 다룸)

쿼리 작업

기본적으로 세 가지 작업의 형식으로 구성된다.

  1. 데이터 소스 가져오기
  2. 쿼리 만들기
  3. 쿼리 실행
class IntroToLINQ
{
    static void Main()
    {
        // The Three Parts of a LINQ Query:
        // 1. Data source.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Query creation.
        // numQuery is an IEnumerable<int>
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;

        // 3. Query execution.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }
    }
}

1번 데이터 소스 가져오기

예제의 데이터 소스는 배열이기 때문에 제네릭 IEnumerable 인터페이스를 암시적으로 지원합니다.

즉, LINQ로 쿼리할 수 있다는 의미이다.

이 말은 반대로 쿼리를 하기 위해선 IEnumerable 이거나 IQueryable이어야 한다.

이러한 형식을 쿼리 가능 형식이라고 한다.

IEnumerable, IQueryable에 관한 내용은 아래 포스팅에서 참고

2번 쿼리 만들기

쿼리식은 기본적으로 from, where, select의 세 가지 절이 포함된다.

앞서 다룬 특징 처럼 실제 쿼리가 동작하는 곳은 2번이 아닌 3번이다.

이러한 개념 지연된 실행이라고 한다

이러한 지연된 실행은 유용하게 작용할 수 있는데 쿼리식을 담아놓은 형태로 나중에 데이터 수정 후 수정된 데이터에 원래 쿼리식의 사용이 가능해진다.

++물론 이걸 모르면 문제가 될 수 있다.

좀 더 알아보면 실제로 쿼리 명령을 저장할 뿐 실행은 foreach에서 이루어진다.

  • from: 데이터 소스를 가져와 하나씩 꺼내는 작업
  • where: 해당 데이터 소스에서 값을 꺼낼 조건
  • select: 추출할 데이터

위 내용을 모르고 봐도 직관적인 이해가 가능하다.

다르게 메서드형태로도 작성이 가능하지만 위 처럼 LINQ형식을 사용하는 것이 직관성에 좋다.

이외에도 다양한 조건 식이나 그룹에 관련된 내용이 있지만 아래 참고에서 찾아보길 권한다.

++ from에서 설정한 num이라는 값은 foreach문에서 사용하는 변수로 범위변수또는 쿼리변수라고 한다.

from에서 시작하여 selet또는 group으로 끝나는게 기본 양식이며 사이에는 쿼리 연산자가 들어갈 수 있다.

Linq에는 50개이상의 표준 쿼리 연산자가 있다..

3번 쿼리 실행

실제 사용하기 전까지 수행되지 않기 때문에

Count, Max, Average, First같은 함수들은 이러한 쿼리는 단일 값을 반환하지만 즉시 실행한다.

var evenNumQuery =
    from num in numbers
    where (num % 2) == 0
    select num;

int evenNumCount = evenNumQuery.Count();

다른 예로 해당 결과를 받기 위해선 ToList 또는 ToArray를 호출할 수 있다.

List<int> numQuery2 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToList();

// or like this:
// numQuery3 is still an int[]

var numQuery3 =
    (from num in numbers
     where (num % 2) == 0
     select num).ToArray();

2번에선 해당 쿼리에 대한 정보를 저장하고 실제로 뽑는 과정에서 동작한다.

현재는 var형식이지만 쿼리를 담기위해선 IEnumerable, IQueryable같은 파생된 인터페이스를 지원하는 형식을 가져야 한다.

즉, 쿼리 가능 형식이어야 한다.

실제 작업 예제

지금 진행 중인 프로젝트에서 엑셀 데이터를 json 형식으로 파싱하여 관리하는데

해당 날짜와 장소에 맞게 데이터를 보내주는 퍼블릭API를 만들었다.

public List<PlaceDialogDBEntity> GetPlaceDialogData(int curDay, Enums.MoveButton curPlace)
{
    List<PlaceDialogDBEntity> placeDialogList = new List<PlaceDialogDBEntity>();
    
    for (int i = 0; i < data.PlaceEntity.Count; i++)
    {
        if (data.PlaceEntity[i].day == curDay && data.PlaceEntity[i].place == curPlace)
        {
            placeDialogList.Add(data.PlaceEntity[i]);
        }
    }

    return placeDialogList;
}

원래 작성된 코드의 일부분이다.

현재 날짜와 장소를 입력받아서 순회하여 맞는 데이터를 배열에 넣어주고 반환한다.

사실 코드가 그렇게 길지 않아서 문제가 없는 것 같지만 아래 Linq로 작성된 형식을 보면 좀 더 가독성, 간결성을 확보했다.

public List<PlaceDialogDBEntity> GetPlaceDialogData(int curDay, Enums.MoveButton curPlace)
{
    List<PlaceDialogDBEntity> placeDialogList = (from dialogDB in data.PlaceEntity
        where dialogDB.day == curDay && dialogDB.place == curPlace
        select dialogDB).ToList();

    return placeDialogList;
}

쿼리식을 바로 실행하여 반환하기 위해 바로 ToList로 반환하는 모습이다.

느낀점

쿼리에 대한 활용성은 매우매우 좋기 때문에 사용하는 걸 추천한다.

하지만 공부해야하는 양이 많기 때문에 그때 그때 필요한 메서드 위주로 찾아보고 활용하는 걸 추천..


참고

댓글남기기