표준 쿼리 연산자 개요
표준 쿼리 연산자는 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# 구문에 대한 제한 사항이 있습니다. 또한 IQueryProvider
은 EF Core와 같이 더 많은 제한을 가할 수 있습니다. 데이터 원본에 대한 설명서를 확인합니다.
이러한 메서드의 대부분은 시퀀스에서 작동합니다. 여기서 시퀀스는 형식이 IEnumerable<T> 인터페이스 또는 IQueryable<T> 인터페이스를 구현하는 개체입니다. 표준 쿼리 연산자는 필터링, 프로젝션, 집계, 정렬 등을 비롯한 쿼리 기능을 제공합니다. 각 집합을 구성하는 메서드는 각각 Enumerable 및 Queryable 클래스의 정적 멤버입니다. 이는 작동하는 형식에 대한 확장 메서드로 정의됩니다.
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
에 대한 참조가 있습니다.
원본 리포지토리데이터 집합을 찾을 수 있습니다.
쿼리 연산자 유형
표준 쿼리 연산자는 싱글톤 값 또는 값 시퀀스를 반환하는지 여부에 따라 실행 타이밍이 다릅니다. 싱글톤 값(예: Average 및 Sum)을 반환하는 메서드는 즉시 실행됩니다. 시퀀스를 반환하는 메서드는 쿼리 실행을 연기하고 열거 가능한 개체를 반환합니다. 한 쿼리의 출력 시퀀스를 다른 쿼리에 대한 입력 시퀀스로 사용할 수 있습니다. 쿼리 메서드에 대한 호출은 하나의 쿼리에서 함께 연결될 수 있으므로 쿼리가 임의로 복잡해질 수 있습니다.
쿼리 연산자
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를 쿼리하고 절 방법을 참조하세요.
데이터 원본을 가져오면 해당 데이터 원본에 대해 여러 작업을 수행할 수 있습니다.
-
where
키워드를 사용하여 데이터 필터링합니다. -
데이터는
orderby
와 선택적으로descending
키워드를 사용하여 정렬합니다. -
데이터을
group
및 필요에 따라into
키워드를 사용하여 그룹화합니다. -
데이터를
join
키워드를 사용하여과 조인합니다. -
select
키워드를 사용하여 Project 데이터 .
쿼리 식 구문 테이블
다음 표에서는 동일한 쿼리 식 절이 있는 표준 쿼리 연산자를 나열합니다.
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 공급자는 조인 후에 해당 순서를 유지하지 않을 수 있습니다. 자세한 내용은 조인 절참조하십시오.
참고하십시오
.NET