LINQ 쿼리 소개(C#)Introduction to LINQ Queries (C#)

쿼리는 데이터 소스에서 데이터를 검색하는 식입니다.A query is an expression that retrieves data from a data source. 쿼리는 일반적으로 특수화된 쿼리 언어로 표현됩니다.Queries are usually expressed in a specialized query language. 관계형 데이터베이스에는 SQL이 사용되고 XML에는 XQuery가 사용되는 것처럼 시간에 따라 다양한 형식의 데이터 소스에 대해 서로 다른 언어가 개발되었습니다.Different languages have been developed over time for the various types of data sources, for example SQL for relational databases and XQuery for XML. 따라서 개발자는 지원해야 하는 데이터 소스의 형식이나 데이터 형식에 따라 새로운 쿼리 언어를 배워야 했습니다.Therefore, developers have had to learn a new query language for each type of data source or data format that they must support. LINQLINQ는 다양한 데이터 소스 및 형식에 사용할 수 있는 일관된 모델을 제공함으로써 이러한 상황을 간단하게 합니다. simplifies this situation by offering a consistent model for working with data across various kinds of data sources and formats. LINQLINQ 쿼리에서는 항상 개체를 사용하고 있습니다.In a LINQLINQ query, you are always working with objects. XML 문서, SQL 데이터베이스, ADO.NETADO.NET 데이터 집합, .NET 컬렉션 및 LINQLINQ 공급자를 사용할 수 있는 다른 모든 형식에서 데이터를 쿼리하고 변환하는 데 동일한 기본 코딩 패턴을 사용합니다.You use the same basic coding patterns to query and transform data in XML documents, SQL databases, ADO.NETADO.NET Datasets, .NET collections, and any other format for which a LINQLINQ provider is available.

쿼리 작업의 세 부분Three Parts of a Query Operation

모든 LINQLINQ 쿼리 작업은 다음과 같은 세 가지 고유한 작업으로 구성됩니다.All LINQLINQ query operations consist of three distinct actions:

  1. 데이터 소스 가져오기.Obtain the data source.

  2. 쿼리 만들기.Create the query.

  3. 쿼리 실행.Execute the query.

다음 예제에서는 쿼리 작업의 세 부분이 소스 코드로 표현되는 방식을 보여 줍니다.The following example shows how the three parts of a query operation are expressed in source code. 예제에서는 편의상 정수 배열을 데이터 소스로 사용하지만 다른 데이터 소스에도 동일한 개념이 적용됩니다.The example uses an integer array as a data source for convenience; however, the same concepts apply to other data sources also. 이 예제는 이 항목의 나머지 부분 전체에서 참조됩니다.This example is referred to throughout the rest of this topic.

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);
        }
    }
}

다음 그림에서는 전체 쿼리 작업을 보여 줍니다.The following illustration shows the complete query operation. LINQLINQ에서는 쿼리 실행이 쿼리 자체와 구분됩니다. 즉, 쿼리 변수를 만드는 것만으로 데이터가 검색되지는 않습니다.In LINQLINQ the execution of the query is distinct from the query itself; in other words you have not retrieved any data just by creating a query variable.

완전한 LINQ 쿼리 작업Complete LINQ Query Operation

데이터 소스The Data Source

이전 예제에서는 데이터 소스가 배열이기 때문에 제네릭 IEnumerable<T> 인터페이스를 암시적으로 지원합니다.In the previous example, because the data source is an array, it implicitly supports the generic IEnumerable<T> interface. 즉, LINQLINQ로 쿼리할 수 있다는 의미입니다.This fact means it can be queried with LINQLINQ. 쿼리가 foreach 문에서 실행되고, foreachIEnumerable 또는 IEnumerable<T>이 필요합니다.A query is executed in a foreach statement, and foreach requires IEnumerable or IEnumerable<T>. IEnumerable<T> 또는 제네릭 IQueryable<T> 같은 파생된 인터페이스를 지원하는 형식을 쿼리 가능 형식이라고 합니다.Types that support IEnumerable<T> or a derived interface such as the generic IQueryable<T> are called queryable types.

쿼리 가능 형식은 LINQLINQ 데이터 소스로 사용하기 위해 수정하거나 특별하게 처리할 필요가 없습니다.A queryable type requires no modification or special treatment to serve as a LINQLINQ data source. 소스 데이터가 쿼리 가능 형식으로 메모리에 나타나지 않을 경우 LINQLINQ 공급자는 그렇게 나타내야 합니다.If the source data is not already in memory as a queryable type, the LINQLINQ provider must represent it as such. 예를 들어 LINQ to XML는 XML 문서를 쿼리 가능 XElement 형식으로 로드합니다.For example, LINQ to XML loads an XML document into a queryable XElement type:

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

먼저 LINQ to SQLLINQ to SQL을 사용하여 디자인 타임에 수동으로 또는 Visual Studio에서 Visual Studio의 LINQ to SQL 도구를 사용하여 개체 관계형 매핑을 만듭니다.With LINQ to SQLLINQ to SQL, you first create an object-relational mapping at design time either manually or by using the LINQ to SQL Tools in Visual Studio in Visual Studio. 개체에 대해 쿼리를 작성하면 런타임에 LINQ to SQLLINQ to SQL에서 데이터베이스와의 통신을 처리합니다.You write your queries against the objects, and at run-time LINQ to SQLLINQ to SQL handles the communication with the database. 다음 예에서 Customers는 데이터베이스의 특정 테이블을 나타내며, IQueryable<T> 쿼리 결과 형식은 IEnumerable<T>에서 파생됩니다.In the following example, Customers represents a specific table in the database, and the type of the query result, IQueryable<T>, derives from IEnumerable<T>.

Northwnd db = new Northwnd(@"c:\northwnd.mdf");  

// Query for customers in London.  
IQueryable<Customer> custQuery =  
    from cust in db.Customers  
    where cust.City == "London"  
    select cust;  

특정 형식의 데이터 소스를 만드는 방법에 대한 자세한 내용은 다양한 LINQLINQ 공급자에 대한 설명서를 참조하세요.For more information about how to create specific types of data sources, see the documentation for the various LINQLINQ providers. 그러나 기본 규칙은 아주 간단합니다. LINQLINQ 데이터 소스는 제네릭 IEnumerable<T> 인터페이스 또는 이 인터페이스에서 상속된 인터페이스를 지원하는 모든 개체입니다.However, the basic rule is very simple: a LINQLINQ data source is any object that supports the generic IEnumerable<T> interface, or an interface that inherits from it.

참고

제네릭이 아닌 IEnumerable 인터페이스를 지원하는 ArrayList 같은 형식은 LINQLINQ 데이터 소스로도 사용됩니다.Types such as ArrayList that support the non-generic IEnumerable interface can also be used as a LINQLINQ data source. 자세한 내용은 방법: LINQ를 사용하여 ArrayList 쿼리(C#)를 참조하세요.For more information, see How to: Query an ArrayList with LINQ (C#).

쿼리The Query

쿼리는 데이터 소스 또는 소스에서 검색할 정보를 지정합니다.The query specifies what information to retrieve from the data source or sources. 필요한 경우 쿼리는 정보를 반환하기 전에 해당 정보를 정렬, 그룹화 및 구체화하는 방법도 지정합니다.Optionally, a query also specifies how that information should be sorted, grouped, and shaped before it is returned. 쿼리는 쿼리 변수에 저장되고 쿼리 식으로 초기화됩니다.A query is stored in a query variable and initialized with a query expression. 쿼리를 쉽게 작성할 수 있도록 C#에서는 새로운 쿼리 구문이 도입되었습니다.To make it easier to write queries, C# has introduced new query syntax.

이전 예제의 쿼리는 정수 배열에서 모든 짝수를 반환합니다.The query in the previous example returns all the even numbers from the integer array. 쿼리 식에는 from, whereselect의 세 가지 절이 포함됩니다.The query expression contains three clauses: from, where and select. SQL에 익숙한 경우 절의 순서가 SQL의 순서와 반대임을 알고 있을 것입니다. from 절은 데이터 소스를 지정하고 where 절은 필터를 적용하며 select 절은 반환되는 요소의 형식을 지정합니다.(If you are familiar with SQL, you will have noticed that the ordering of the clauses is reversed from the order in SQL.) The from clause specifies the data source, the where clause applies the filter, and the select clause specifies the type of the returned elements. 이러한 쿼리 절 및 다른 쿼리 절은 LINQ 쿼리 식 섹션에서 자세히 설명합니다.These and the other query clauses are discussed in detail in the LINQ Query Expressions section. 여기에서 중요한 점은 LINQLINQ에서 쿼리 변수 자체는 아무 작업도 수행하지 않고 데이터를 반환하지 않는다는 것입니다.For now, the important point is that in LINQLINQ, the query variable itself takes no action and returns no data. 나중에 쿼리가 실행될 때 결과를 생성하는 데 필요한 정보를 저장합니다.It just stores the information that is required to produce the results when the query is executed at some later point. 백그라운드에서 쿼리를 생성하는 방법에 대한 자세한 내용은 표준 쿼리 연산자 개요(C#)를 참조하세요.For more information about how queries are constructed behind the scenes, see Standard Query Operators Overview (C#).

참고

쿼리는 메서드 구문을 사용하여 표현할 수도 있습니다.Queries can also be expressed by using method syntax. 자세한 내용은 LINQ의 쿼리 구문 및 메서드 구문을 참조하세요.For more information, see Query Syntax and Method Syntax in LINQ.

쿼리 실행Query Execution

지연된 실행Deferred Execution

앞에서 설명한 대로 쿼리 변수 자체는 쿼리 명령을 저장할 뿐입니다.As stated previously, the query variable itself only stores the query commands. 실제 쿼리 실행은 foreach 문에서 쿼리 변수가 반복될 때까지 지연됩니다.The actual execution of the query is deferred until you iterate over the query variable in a foreach statement. 이 개념을 지연된 실행이라고 하며 다음 예제에서 보여 줍니다.This concept is referred to as deferred execution and is demonstrated in the following example:

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

foreach 문은 쿼리 결과가 검색되는 위치이기도 합니다.The foreach statement is also where the query results are retrieved. 예를 들어 이전 쿼리에서 반복 변수 num은 반환된 시퀀스에서 각 값을 한 번에 하나씩 저장합니다.For example, in the previous query, the iteration variable num holds each value (one at a time) in the returned sequence.

쿼리 변수 자체는 쿼리 결과를 저장하지 않으므로 원하는 만큼 자주 실행할 수 있습니다.Because the query variable itself never holds the query results, you can execute it as often as you like. 예를 들어 별도의 응용 프로그램에서 지속적으로 업데이트되는 데이터베이스가 있을 수 있습니다.For example, you may have a database that is being updated continually by a separate application. 이 응용 프로그램에서 최근 데이터를 검색하는 쿼리를 작성하고 이를 일정 간격을 두고 반복적으로 실행하여 매번 다른 결과를 검색할 수 있습니다.In your application, you could create one query that retrieves the latest data, and you could execute it repeatedly at some interval to retrieve different results every time.

즉시 실행 강제 적용Forcing Immediate Execution

소스 요소 범위에 대해 집계 함수를 수행하는 쿼리는 먼저 해당 요소를 반복해야 합니다.Queries that perform aggregation functions over a range of source elements must first iterate over those elements. 이러한 쿼리의 예로 Count, Max, AverageFirst가 있습니다.Examples of such queries are Count, Max, Average, and First. 이러한 쿼리는 쿼리 자체에서 결과를 반환하려면 foreach를 사용해야 하기 때문에 명시적 foreach 문 없이 실행됩니다.These execute without an explicit foreach statement because the query itself must use foreach in order to return a result. 또한 이러한 유형의 쿼리는 IEnumerable 컬렉션이 아니라 단일 값을 반환합니다.Note also that these types of queries return a single value, not an IEnumerable collection. 다음 쿼리는 소스 배열에서 짝수의 개수를 반환합니다.The following query returns a count of the even numbers in the source array:

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

int evenNumCount = evenNumQuery.Count();

모든 쿼리를 즉시 실행하고 그 결과를 캐시하기 위해 ToList 또는 ToArray 메서드를 호출할 수 있습니다.To force immediate execution of any query and cache its results, you can call the ToList or ToArray methods.

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();

또한 foreach 루프를 쿼리 식 바로 다음에 배치하여 강제로 실행할 수 있습니다.You can also force execution by putting the foreach loop immediately after the query expression. 그러나 ToList 또는 ToArray를 호출하여 단일 컬렉션 개체에서 모든 데이터를 캐시할 수도 있습니다.However, by calling ToList or ToArray you also cache all the data in a single collection object.

참고 항목See Also

C#에서 LINQ 시작Getting Started with LINQ in C#
연습: C#에서 쿼리 작성Walkthrough: Writing Queries in C#
연습: C#에서 쿼리 작성Walkthrough: Writing Queries in C#
LINQ 쿼리 식LINQ Query Expressions
foreach, inforeach, in
쿼리 키워드(LINQ)Query Keywords (LINQ)