Compartir a través de


Cómo: Usar árboles de expresión para crear consultas dinámicas (C# y Visual Basic)

En LINQ, los árboles de expresión se utilizan para representar consultas estructuradas para orígenes de datos que implementan IQueryable. Por ejemplo, el proveedor LINQ to SQL implementa la interfaz IQueryable para realizar consultas en almacenes de datos relacionales. Los compiladores de Visual Basic y C# compilan las consultas destinadas a esos orígenes de datos en código que genera un árbol de expresión en tiempo de ejecución. El proveedor de la consulta puede entonces recorrer la estructura de datos del árbol de expresión y traducirla en un lenguaje de consulta apropiado para el origen de datos.

Los árboles de expresión también se utilizan en LINQ para representar expresiones lambda asignadas a variables de tipo Expression.

En este tema se describe cómo utilizar árboles de expresión para crear consultas dinámicas de LINQ. Las consultas dinámicas son útiles cuando no se conocen los detalles de la consulta en tiempo de compilación. Por ejemplo, una aplicación podría proporcionar una interfaz de usuario que permite al usuario final especificar uno o más predicados para filtrar los datos. Para poder usar LINQ en las consultas, este tipo de aplicación debe utilizar árboles de expresión para crear la consulta de LINQ en tiempo de ejecución.

Ejemplo

El ejemplo siguiente muestra cómo utilizar árboles de expresión para construir una consulta contra un origen de datos IQueryable y, a continuación, ejecutar dicha consulta. El código compila un árbol de expresión para representar la consulta siguiente:

Consulta en C#

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

Consulta en Visual Basic

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

Los métodos de generador incluidos en el espacio de nombres System.Linq.Expressions se utilizan para crear árboles de expresión que representan las expresiones que forman la consulta completa. Las expresiones que representan llamadas a métodos de operador de consulta estándar hacen referencia a las implementaciones de Queryable de estos métodos. El árbol de expresión final se pasa a la implementación CreateQuery``1(Expression) del proveedor del origen de datos IQueryable para crear una consulta ejecutable de tipo IQueryable. Los resultados se obtienen enumerando esa variable 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
            */

Este código utiliza un número fijo de expresiones en el predicado que se pasa al método Queryable.Where. Sin embargo, puede escribir una aplicación que combina un número variable de expresiones de predicado que depende de la información proporcionada por el usuario. También puede variar los operadores de consulta estándar a los que se llaman en la consulta, según la información proporcionada por el usuario.

Compilar el código

  • Cree un nuevo proyecto de Aplicación de consola en Visual Studio.

  • Agregue una referencia a System.Core.dll si ésta no existe aún.

  • Incluya el espacio de nombres System.Linq.Expressions.

  • Copie el código del ejemplo y péguelo en el método Main (C#) o en el procedimiento Main Sub (Visual Basic).

Vea también

Tareas

Cómo: Ejecutar árboles de expresión (C# y Visual Basic)

Cómo: Especificar dinámicamente filtros con predicado en tiempo de ejecución (Guía de programación de C#)

Conceptos

Árboles de expresión (C# y Visual Basic)

Otros recursos

LINQ farm seed: Mediante el árbol de expresión Visualizador