Como: usar árvores de expressão para criar consultas dinâmicas (C# e Visual Basic)

Em LINQ, árvores de expressão são usadas para representar as consultas estruturados que usam fontes de dados que implementam IQueryable<T>.Por exemplo, o provedor de LINQ to SQL implementa a interface de IQueryable<T> para consulte armazenamentos de dados relacionais.Os compiladores C# e Visual Basic criar consultas que usam essas fontes de dados no código que cria uma árvore de expressão em tempo de execução.O provedor de consulta pode então atravessar a estrutura de dados da árvore de expressão e traduzi-la em uma linguagem de consulta apropriado para a fonte de dados.

Árvores de expressão também são usadas em LINQ para representar as expressões lambda atribuídas a variáveis do tipo Expression<TDelegate>.

Este tópico descreve como usar as árvores de expressão para criar consultas dinâmicos de LINQ .Consultas dinâmicos são úteis quando as especificidades de uma consulta não são conhecidos em tempo de compilação.Por exemplo, um aplicativo pode fornecer uma interface do usuário que poderia o usuário final para especificar um ou vários predicados para filtrar os dados.Para usar LINQ para ver, esse tipo de aplicativo deve usar árvores de expressão para criar a consulta de LINQ em tempo de execução.

Exemplo

O exemplo a seguir mostra como usar árvores de expressão para construir uma consulta em uma fonte de dados para IQueryable e executa-o.O código cria uma árvore de expressão para representar a consulta a seguir:

Consulta C#

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

Consulta do Visual Basic

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

Os métodos de fábrica no espaço de System.Linq.Expressions são usados para criar árvores de expressão que representam as expressões que compõem a consulta total.Expressões que representam chamadas para métodos padrão de operador de consulta às implementações de Queryable desses métodos.A árvore de expressão final é passada para a implementação de CreateQuery<TElement>(Expression) do provedor de fonte de dados deIQueryable para criar uma consulta executável do tipo IQueryable.Os resultados são obtidos enumerando que variável de consulta.

        ' Add an Imports statement for System.Linq.Expressions.

        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

            // Add a using directive for System.Linq.Expressions.

            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
            */

Esse código usa um número fixo de expressões no predicado que é passado para o método de Queryable.Where .No entanto, você pode escrever um aplicativo que combina um número variável de expressões de predicado que depende de entrada do usuário.Você também pode variar os operadores de consulta padrão que são chamados na consulta, como a entrada do usuário.

Compilando o código

  • Criar um novo projeto de Aplicativo de Console em Visual Studio.

  • Adicione uma referência a System.Core.dll se já não é referenciado.

  • Inclua ao espaço de System.Linq.Expressions.

  • Copie o código de exemplo e cole-o no método de Main (C#) ou procedimento de MainSub (Visual Basic).

Consulte também

Tarefas

Como: executar árvores de expressão (C# e Visual Basic)

Como: especificar dinamicamente o predicado filtros em tempo de execução (guia de programação do C#)

Conceitos

Árvores de expressão (C# e Visual Basic)

Outros recursos

Propagação de farm LINQ: Usando o visualizador da árvore de expressão