다음을 통해 공유


표준 쿼리 연산자 개요

표준 쿼리 연산자는 LINQ 패턴을 형성하는 키워드 및 메서드입니다. C# 언어는 가장 일반적인 쿼리 식에 사용하는 LINQ 쿼리 키워드를 정의합니다. 컴파일러는 이러한 키워드를 사용하여 식을 동등한 메서드 호출로 변환합니다. 두 형식은 동의어입니다. System.Linq 네임스페이스의 일부인 다른 메서드에는 동일한 쿼리 키워드가 없습니다. 이러한 경우 메서드 구문을 사용해야 합니다. 이 섹션에서는 모든 쿼리 연산자 키워드에 대해 설명합니다. 런타임 및 기타 NuGet 패키지는 각 릴리스마다 LINQ 쿼리를 사용하도록 설계된 더 많은 메서드를 추가합니다. 쿼리 키워드에 해당하는 메서드를 포함한 가장 일반적인 메서드는 이 섹션에서 다룹니다. .NET 런타임에서 지원하는 쿼리 메서드의 전체 목록은 System.Linq.Enumerable API 설명서를 참조하세요. 여기서 설명하는 메서드 외에도 이 클래스에는 데이터 원본을 연결하고 합계, 평균 또는 기타 값과 같은 데이터 원본에서 단일 값을 계산하는 메서드가 포함되어 있습니다.

중요합니다

이 샘플은 System.Collections.Generic.IEnumerable<T> 데이터 원본을 사용합니다. System.Linq.IQueryProvider 기반 데이터 원본은 System.Linq.IQueryable<T> 데이터 원본과 식 트리를 사용합니다. 식 트리에는 허용되는 C# 구문에 대한 제한 사항이 있습니다. 또한 IQueryProviderEF Core와 같이 더 많은 제한을 가할 수 있습니다. 데이터 원본에 대한 설명서를 확인합니다.

이러한 메서드의 대부분은 시퀀스에서 작동합니다. 여기서 시퀀스는 형식이 IEnumerable<T> 인터페이스 또는 IQueryable<T> 인터페이스를 구현하는 개체입니다. 표준 쿼리 연산자는 필터링, 프로젝션, 집계, 정렬 등을 비롯한 쿼리 기능을 제공합니다. 각 집합을 구성하는 메서드는 각각 EnumerableQueryable 클래스의 정적 멤버입니다. 이는 작동하는 형식에 대한 확장 메서드로 정의됩니다.

IEnumerable<T> 시퀀스와 IQueryable<T> 시퀀스를 구분하면 런타임에 쿼리가 실행되는 방법이 결정됩니다.

IEnumerable<T>경우 반환된 열거 가능한 개체는 메서드에 전달된 인수를 캡처합니다. 해당 개체가 열거되면 쿼리 연산자의 논리가 사용되며 쿼리 결과가 반환됩니다.

IQueryable<T>의 경우 쿼리가 식 트리으로 변환된 것입니다. 데이터 원본이 쿼리를 최적화할 수 있는 경우 식 트리를 네이티브 쿼리로 변환할 수 있습니다. Entity Framework와 같은 라이브러리는 LINQ 쿼리를 데이터베이스에서 실행되는 네이티브 SQL 쿼리로 변환할 있습니다.

다음 코드 예제에서는 표준 쿼리 연산자를 사용하여 시퀀스에 대한 정보를 가져오는 방법을 보여 줍니다.

string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');

// Using query expression syntax.
var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };

// Using method-based query syntax.
var query2 = words.
    GroupBy(w => w.Length, w => w.ToUpper()).
    Select(g => new { Length = g.Key, Words = g }).
    OrderBy(o => o.Length);

foreach (var obj in query)
{
    Console.WriteLine($"Words of length {obj.Length}:");
    foreach (string word in obj.Words)
        Console.WriteLine(word);
}

// This code example produces the following output:
//
// Words of length 3:
// THE
// FOX
// THE
// DOG
// Words of length 4:
// OVER
// LAZY
// Words of length 5:
// QUICK
// BROWN
// JUMPS

가능한 경우 이 섹션의 쿼리는 입력 소스로 단어 또는 숫자 시퀀스를 사용합니다. 개체 간의 관계가 더 복잡한 쿼리의 경우 학교를 모델링하는 다음 원본이 사용됩니다.

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}

Student에는 학년 수준, 기본 부서 및 일련의 점수가 있습니다. Teacher에는 교사가 수업을 진행하는 캠퍼스를 식별하는 City 속성도 있습니다. Department에는 이름이 있고 부서장 역할을 하는 Teacher에 대한 참조가 있습니다.

원본 리포지토리데이터 집합을 찾을 수 있습니다.

쿼리 연산자 유형

표준 쿼리 연산자는 싱글톤 값 또는 값 시퀀스를 반환하는지 여부에 따라 실행 타이밍이 다릅니다. 싱글톤 값(예: AverageSum)을 반환하는 메서드는 즉시 실행됩니다. 시퀀스를 반환하는 메서드는 쿼리 실행을 연기하고 열거 가능한 개체를 반환합니다. 한 쿼리의 출력 시퀀스를 다른 쿼리에 대한 입력 시퀀스로 사용할 수 있습니다. 쿼리 메서드에 대한 호출은 하나의 쿼리에서 함께 연결될 수 있으므로 쿼리가 임의로 복잡해질 수 있습니다.

쿼리 연산자

LINQ 쿼리에서 첫 번째 단계는 데이터 원본을 지정하는 것입니다. LINQ 쿼리에서 from 절은 데이터 원본(students) 및 범위 변수(student)를 도입하기 위해 먼저 제공됩니다.

//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
                        select student;

범위 변수는 쿼리 식에서 실제 반복이 발생하지 않는다는 점을 제외하고 foreach 루프의 반복 변수와 같습니다. 쿼리가 실행되면 범위 변수는 students각 연속 요소에 대한 참조 역할을 합니다. 컴파일러는 student형식을 유추할 수 있으므로 명시적으로 지정할 필요가 없습니다. let 절에 더 많은 범위 변수를 도입할 수 있습니다. 자세한 내용은 let 절참조하세요.

비고

ArrayList같은 제네릭이 아닌 데이터 원본의 경우 범위 변수를 명시적으로 입력해야 합니다. 자세한 내용은 LINQ(C#) 사용하여 ArrayList를 쿼리하고 절 방법을 참조하세요.

데이터 원본을 가져오면 해당 데이터 원본에 대해 여러 작업을 수행할 수 있습니다.

쿼리 식 구문 테이블

다음 표에서는 동일한 쿼리 식 절이 있는 표준 쿼리 연산자를 나열합니다.

메서드 C# 쿼리 식 구문
Cast 명시적으로 형식화된 범위 변수를 사용합니다.

from int i in numbers

(자세한 내용은 절 참조하세요.)
GroupBy group … by

-또는-

group … by … into …

(자세한 내용은 그룹 절참조하세요.)
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) join … in … on … equals … into …

(자세한 내용은 조인 절을 참조하세요.)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(자세한 내용은 조인 절을 참조하세요.)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(자세한 내용은 orderby 절을 참조하세요.)
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(자세한 내용은 정렬 기준 절을 참조하십시오.)
Select select

(자세한 내용은 select 절을 참조하세요.)
SelectMany 여러 from 절입니다.

(자세한 내용은 절 참조하세요.)
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(자세한 내용은 orderby 절을 참조하세요.)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

자세한 내용은 의 정렬 순서 절을 참조하세요.
Where where

자세한 내용은 절에서절을 참조하세요.

LINQ를 사용한 데이터 변환

LINQ(Language-Integrated Query)는 데이터 검색에만 국한되지 않습니다. 또한 데이터를 변환하기 위한 강력한 도구이기도 합니다. LINQ 쿼리를 사용하면 소스 시퀀스를 입력으로 사용하고 여러 가지 방법으로 수정하여 새 출력 시퀀스를 만들 수 있습니다. 정렬 및 그룹화하여 요소 자체를 수정하지 않고도 시퀀스 자체를 수정할 수 있습니다. 그러나 LINQ 쿼리의 가장 강력한 기능은 새 형식을 만드는 기능일 것입니다. select 절은 입력 요소에서 출력 요소를 만듭니다. 입력 요소를 출력 요소로 변환하는 데 사용합니다.

  • 여러 입력 시퀀스를 새 형식의 단일 출력 시퀀스에 병합합니다.
  • 소스 시퀀스에서 각 요소의 하나 또는 여러 속성으로만 구성된 출력 시퀀스를 만듭니다.
  • 소스 데이터에 대해 수행된 작업의 결과로 요소가 구성된 출력 시퀀스를 만듭니다.
  • 다른 형식으로 출력 시퀀스를 만듭니다. 예를 들어 SQL 행 또는 텍스트 파일에서 XML로 데이터를 변환할 수 있습니다.

이러한 변환은 동일한 쿼리에서 다양한 방법으로 결합될 수 있습니다. 또한 한 쿼리의 출력 시퀀스를 새 쿼리의 입력 시퀀스로 사용할 수 있습니다. 다음 예제에서는 메모리 내 데이터 구조의 개체를 XML 요소로 변환합니다.


// Create the query.
var studentsToXML = new XElement("Root",
    from student in students
    let scores = string.Join(",", student.Scores)
    select new XElement("student",
                new XElement("First", student.FirstName),
                new XElement("Last", student.LastName),
                new XElement("Scores", scores)
            ) // end "student"
        ); // end "Root"

// Execute the query.
Console.WriteLine(studentsToXML);

이 코드는 다음 XML 출력을 생성합니다.

<Root>
  <student>
    <First>Svetlana</First>
    <Last>Omelchenko</Last>
    <Scores>97,90,73,54</Scores>
  </student>
  <student>
    <First>Claire</First>
    <Last>O'Donnell</Last>
    <Scores>56,78,95,95</Scores>
  </student>
  ...
  <student>
    <First>Max</First>
    <Last>Lindgren</Last>
    <Scores>86,88,96,63</Scores>
  </student>
  <student>
    <First>Arina</First>
    <Last>Ivanova</Last>
    <Scores>93,63,70,80</Scores>
  </student>
</Root>

자세한 내용은 C#에서 XML 트리 만들기(LINQ to XML) 참조하세요.

한 쿼리의 결과를 후속 쿼리의 데이터 원본으로 사용할 수 있습니다. 이 예제에서는 조인 작업의 결과를 정렬하는 방법을 보여줍니다. 이 쿼리는 그룹 조인을 만든 다음 여전히 범위에 있는 범주 요소를 기반으로 그룹을 정렬합니다. 익명 형식 이니셜라이저 내에서 하위 쿼리는 제품 시퀀스에서 일치하는 모든 요소를 정렬합니다.

var orderedQuery = from department in departments
                   join student in students on department.ID equals student.DepartmentID into studentGroup
                   orderby department.Name
                   select new
                   {
                       DepartmentName = department.Name,
                       Students = from student in studentGroup
                                  orderby student.LastName
                                    select student
                   };

foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}
/* Output:
Chemistry
  Balzan     Josephine
  Fakhouri   Fadi
  Popov      Innocenty
  Seleznyova Sofiya
  Vella      Carmen
Economics
  Adams      Terry
  Adaobi     Izuchukwu
  Berggren   Jeanette
  Garcia     Cesar
  Ifeoma     Nwanneka
  Jamuike    Ifeanacho
  Larsson    Naima
  Svensson   Noel
  Ugomma     Ifunanya
Engineering
  Axelsson   Erik
  Berg       Veronika
  Engström   Nancy
  Hicks      Cassie
  Keever     Bruce
  Micallef   Nicholas
  Mortensen  Sven
  Nilsson    Erna
  Tucker     Michael
  Yermolayeva Anna
English
  Andersson  Sarah
  Feng       Hanying
  Ivanova    Arina
  Jakobsson  Jesper
  Jensen     Christiane
  Johansson  Mark
  Kolpakova  Nadezhda
  Omelchenko Svetlana
  Urquhart   Donald
Mathematics
  Frost      Gaby
  Garcia     Hugo
  Hedlund    Anna
  Kovaleva   Katerina
  Lindgren   Max
  Maslova    Evgeniya
  Olsson     Ruth
  Sammut     Maria
  Sazonova   Anastasiya
Physics
  Åkesson    Sami
  Edwards    Amy E.
  Falzon     John
  Garcia     Debra
  Hansson    Sanna
  Mattsson   Martina
  Richardson Don
  Zabokritski Eugene
*/

메서드 구문을 사용하는 동일한 쿼리는 다음 코드에 나와 있습니다.

var orderedQuery = departments
    .GroupJoin(students, department => department.ID, student => student.DepartmentID,
    (department, studentGroup) => new
    {
        DepartmentName = department.Name,
        Students = studentGroup.OrderBy(student => student.LastName)
    })
    .OrderBy(department => department.DepartmentName);


foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}

조인 전에 하나 이상의 소스 시퀀스와 함께 orderby 절을 사용할 수 있지만 일반적으로 권장하지 않습니다. 일부 LINQ 공급자는 조인 후에 해당 순서를 유지하지 않을 수 있습니다. 자세한 내용은 조인 절참조하십시오.

참고하십시오