다음을 통해 공유


방법: 식 트리를 사용하여 동적 쿼리 빌드(C# 및 Visual Basic)

LINQ에서는 IQueryable<T>을 구현하는 데이터의 소스를 대상으로 하는 구조화된 쿼리를 나타내기 위해 식 트리를 사용합니다. 예를 들어 LINQ to SQL 공급자는 관계형 데이터 저장소를 쿼리하는 IQueryable<T> 인터페이스를 구현합니다. C# 및 Visual Basic 컴파일러는 이러한 데이터 소스를 대상하는 쿼리를 런타임에 식 트리를 빌드하는 코드로 컴파일합니다. 그러면 쿼리 공급자가 식 트리 데이터 구조를 트래버스하여 데이터 소스에 적합한 쿼리 언어로 변환할 수 있습니다.

식 트리는 LINQ에서 Expression<TDelegate> 형식 변수에 할당되는 람다 식을 나타내는 데에도 사용됩니다.

이 항목에서는 식 트리를 사용하여 동적 LINQ 쿼리를 만드는 방법에 대해 설명합니다. 동적 쿼리는 컴파일 타임에 쿼리의 세부 사항을 알 수 없는 경우 유용하게 사용할 수 있습니다. 예를 들어 응용 프로그램에서 최종 사용자가 하나 이상의 조건자를 지정하여 데이터를 필터링할 수 있는 사용자 인터페이스를 제공할 수 있습니다. 이런 종류의 응용 프로그램에서 쿼리에 LINQ를 사용하려면 식 트리를 사용하여 런타임에 LINQ 쿼리를 만들어야 합니다.

예제

다음 예제에서는 식 트리를 사용하여 IQueryable 데이터 소스에 대한 쿼리를 구성한 후 실행하는 방법을 보여 줍니다. 코드에서는 다음과 같은 쿼리를 나타내는 식 트리를 빌드합니다.

C# 쿼리

companies.Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).OrderBy(company => company)

Visual Basic 쿼리

companies.Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16).OrderBy(Function(company) company)

System.Linq.Expressions 네임스페이스의 팩터리 메서드는 전체 쿼리를 구성하는 식을 나타내는 식 트리를 만드는 데 사용됩니다. 표준 쿼리 연산자 메서드에 대한 호출을 나타내는 식에서는 이러한 메서드의 Queryable 구현을 참조합니다. 최종 식 트리는 IQueryable 데이터 소스 공급자의 CreateQuery<TElement>(Expression) 구현으로 전달되어 IQueryable 형식의 실행 가능한 쿼리를 만듭니다. 결과는 이 쿼리 변수를 열거하여 얻습니다.

        Dim companies = 
            {"Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light", 
             "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works", 
             "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders", 
             "Blue Yonder Airlines", "Trey Research", "The Phone Company", 
             "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee"}

        ' The IQueryable data to query.
        Dim queryableData As IQueryable(Of String) = companies.AsQueryable()

        ' Compose the expression tree that represents the parameter to the predicate.
        Dim pe As ParameterExpression = Expression.Parameter(GetType(String), "company")

        ' ***** Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16) *****
        ' Create an expression tree that represents the expression: company.ToLower() = "coho winery".
        Dim left As Expression = Expression.Call(pe, GetType(String).GetMethod("ToLower", System.Type.EmptyTypes))
        Dim right As Expression = Expression.Constant("coho winery")
        Dim e1 As Expression = Expression.Equal(left, right)

        ' Create an expression tree that represents the expression: company.Length > 16.
        left = Expression.Property(pe, GetType(String).GetProperty("Length"))
        right = Expression.Constant(16, GetType(Integer))
        Dim e2 As Expression = Expression.GreaterThan(left, right)

        ' Combine the expressions to create an expression tree that represents the
        ' expression: company.ToLower() = "coho winery" OrElse company.Length > 16).
        Dim predicateBody As Expression = Expression.OrElse(e1, e2)

        ' Create an expression tree that represents the expression:
        ' queryableData.Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16)
        Dim whereCallExpression As MethodCallExpression = Expression.Call( 
                GetType(Queryable), 
                "Where", 
                New Type() {queryableData.ElementType}, 
                queryableData.Expression, 
                Expression.Lambda(Of Func(Of String, Boolean))(predicateBody, New ParameterExpression() {pe}))
        ' ***** End Where *****

        ' ***** OrderBy(Function(company) company) *****
        ' Create an expression tree that represents the expression:
        ' whereCallExpression.OrderBy(Function(company) company)
        Dim orderByCallExpression As MethodCallExpression = Expression.Call( 
                GetType(Queryable), 
                "OrderBy", 
                New Type() {queryableData.ElementType, queryableData.ElementType}, 
                whereCallExpression, 
                Expression.Lambda(Of Func(Of String, String))(pe, New ParameterExpression() {pe}))
        ' ***** End OrderBy *****

        ' Create an executable query from the expression tree.
        Dim results As IQueryable(Of String) = queryableData.Provider.CreateQuery(Of String)(orderByCallExpression)

        ' Enumerate the results.
        For Each company As String In results
            Console.WriteLine(company)
        Next

        ' This code produces the following output:
        '
        ' Blue Yonder Airlines
        ' City Power & Light
        ' Coho Winery
        ' Consolidated Messenger
        ' Graphic Design Institute
        ' Humongous Insurance
        ' Lucerne Publishing
        ' Northwind Traders
        ' The Phone Company
        ' Wide World Importers

            string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
                               "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
                               "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
                               "Blue Yonder Airlines", "Trey Research", "The Phone Company",
                               "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };

            // The IQueryable data to query.
            IQueryable<String> queryableData = companies.AsQueryable<string>();

            // Compose the expression tree that represents the parameter to the predicate.
            ParameterExpression pe = Expression.Parameter(typeof(string), "company");

            // ***** Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) *****
            // Create an expression tree that represents the expression 'company.ToLower() == "coho winery"'.
            Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
            Expression right = Expression.Constant("coho winery");
            Expression e1 = Expression.Equal(left, right);

            // Create an expression tree that represents the expression 'company.Length > 16'.
            left = Expression.Property(pe, typeof(string).GetProperty("Length"));
            right = Expression.Constant(16, typeof(int));
            Expression e2 = Expression.GreaterThan(left, right);

            // Combine the expression trees to create an expression tree that represents the
            // expression '(company.ToLower() == "coho winery" || company.Length > 16)'.
            Expression predicateBody = Expression.OrElse(e1, e2);

            // Create an expression tree that represents the expression
            // 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
            MethodCallExpression whereCallExpression = Expression.Call(
                typeof(Queryable),
                "Where",
                new Type[] { queryableData.ElementType },
                queryableData.Expression,
                Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
            // ***** End Where *****

            // ***** OrderBy(company => company) *****
            // Create an expression tree that represents the expression
            // 'whereCallExpression.OrderBy(company => company)'
            MethodCallExpression orderByCallExpression = Expression.Call(
                typeof(Queryable),
                "OrderBy",
                new Type[] { queryableData.ElementType, queryableData.ElementType },
                whereCallExpression,
                Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
            // ***** End OrderBy *****

            // Create an executable query from the expression tree.
            IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

            // Enumerate the results.
            foreach (string company in results)
                Console.WriteLine(company);

            /*  This code produces the following output:

                Blue Yonder Airlines
                City Power & Light
                Coho Winery
                Consolidated Messenger
                Graphic Design Institute
                Humongous Insurance
                Lucerne Publishing
                Northwind Traders
                The Phone Company
                Wide World Importers
            */

이 코드에서는 Queryable.Where 메서드로 전달되는 조건자에서 고정된 수의 식을 사용합니다. 하지만 사용자 입력에 따라 다양한 수의 조건자 식을 조합하는 응용 프로그램을 작성할 수도 있습니다. 또한 사용자 입력에 따라 쿼리에서 호출되는 표준 쿼리 연산자를 변경할 수도 있습니다.

코드 컴파일

  • Visual Studio에서 새 콘솔 응용 프로그램 프로젝트를 만듭니다.

  • 이미 참조되지 않았다면 System.Core.dll에 대한 참조를 추가합니다.

  • System.Linq.Expressions 네임스페이스를 포함합니다.

  • 예제의 코드를 복사한 다음 Main 메서드(C#) 또는 Main Sub 프로시저(Visual Basic)에 붙여넣습니다.

참고 항목

작업

방법: 식 트리 실행(C# 및 Visual Basic)

방법: 런타임에 동적으로 조건자 필터 지정(C# 프로그래밍 가이드)

개념

식 트리(C# 및 Visual Basic)

기타 리소스

LINQ Farm Seed: Using the Expression Tree Visualizer