Cree una cláusula where totalmente dinámica con el árbol de expresiones y ejecútelo en IQueryable

c# expression-trees iqueryable lambda where

Pregunta

En el punto (3) de mi código, definí una consulta llamada query1 en la que definí una expresión .Where lambda. Esta consulta es de alguna manera dinámica pero aún contiene elementos estáticos, siempre se refiere al Tipo Empleado y su propiedad (int) ClientID.

Ahora me gustaría mucho hacer que la referencia al tipo y su propiedad sea dinámica, en función de los parámetros del método que se muestran a continuación en el punto (1).

Lo que intenté hasta ahora es hacer que la parte estática de la consulta definida en el punto (3) sea completamente dinámica reemplazándola con un árbol de expresiones más elaborado como está escrito en (4), (5) y (6). Pero cuando intento agregar todo junto, dice que llamo .Where con parámetros incorrectos. No sé cómo llamar. Con los parámetros correctos para crear una selección completamente dinámica.

¿Alguien sabe resolver este problema? He pasado un día buscando y no he encontrado una solución hasta ahora.

        dsMain domainService = new dsMain();


        //(1)i want to rewrite the following four variables to method-parameters
        Type entityType = typeof(Employee);
        String targetProperty = "ClientID";
        Type entityProperty = typeof(Employee).GetProperty(targetProperty).PropertyType;
        int idToDelete = 5;


        //(2)create expression-function: idToDelete == entityType.targetProperty (in this case: Employee.ClientID)
        ParameterExpression numParam = Expression.Parameter(entityProperty, targetProperty.Substring(0, 3));
        ConstantExpression equalTarget = Expression.Constant(idToDelete, idToDelete.GetType());
        BinaryExpression intEqualsID = Expression.Equal(numParam, equalTarget);
        Expression<Func<int, bool>> lambda1 =
                    Expression.Lambda<Func<int, bool>>(
                    intEqualsID,
                    new ParameterExpression[] { numParam });

        //(3)I want to create query1 fully dynamic, so defining Employee or an other type and its property at run time
        WhereClause = lambda1.Compile();
        IQueryable<Employee> employees = domainService.GetEmployees();
        var query1 = employees.Where<Employee>(C => WhereClause.Invoke(C.ClientID)).Expression;



        //(4)create the operand body {value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
        var operandbodyMethod = WhereClause.GetType().GetMethod("Invoke");
        var operandbodyType = typeof(System.Boolean);
        var operandbodyArgs1Expression = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
        var operandbodyArgs1 = Expression.MakeMemberAccess(operandbodyArgs1Expression, entityType.GetMember(targetProperty)[0]);
        var operandBodyObjectExp = Expression.Constant(this, this.GetType());
        var operandbodyObject = Expression.MakeMemberAccess(operandBodyObjectExp, this.GetType().GetMember("WhereClause")[0]);

        //(5)create the operand {E => value(ASP.test_aspx).WhereClause.Invoke(E.ClientID)}
        var operandbody = Expression.Call(operandbodyObject, operandbodyMethod, operandbodyArgs1);
        var operandParameter = Expression.Parameter(entityType, entityType.Name.Substring(0, 1));
        var operandType = typeof(Func<,>).MakeGenericType(entityType, typeof(System.Boolean));

        //(6)
        var operand = Expression.Lambda(operandType, operandbody, new ParameterExpression[] { operandParameter });
        var expressionType = typeof(Expression<>).MakeGenericType(operandType);
        var completeWhereExpression = Expression.MakeUnary(ExpressionType.Quote, operand, expressionType);


        //(7)the line below does not work
        var query2 = employees.Where<Employee>(completeWhereExpression).Expression;

Muchas gracias por leer mi pregunta! Si tiene preguntas sobre mi pregunta, por favor pregúnteles :)

Respuesta aceptada

Esto es bastante difícil de ver de forma aislada, pero lo primero que ocurre es que Compile parece fuera de lugar para IQueryable , que rara vez funcionará (LINQ-to-Objects es la excepción).

Un equivalente a WhereClause.Invoke(C.ClientID) es usar Expression.Invoke para llamar a una subexpresión, pero incluso esto es un error: LINQ-to-SQL lo admitirá, EF (en 3.5 por lo menos) no ( tal vez "no"; no he vuelto a comprobar en 4.0). En última instancia, sería más robusto crear lambda1 como una Expression<Func<Employee,bool>> si es posible:

    ParameterExpression empParam = Expression.Parameter(typeof(Employee),"emp");
    ConstantExpression equalTarget = Expression.Constant(idToDelete, idToDelete.GetType());
    BinaryExpression intEqualsID = Expression.Equal(
        Expression.PropertyOrField(empParam, targetProperty), equalTarget);
    Expression<Func<Exmployee, bool>> lambda1 =
                Expression.Lambda<Func<int, bool>>(
                intEqualsID,
                empParam);

Luego pasa esto a Where :

var query1 = employees.Where(lambda1);


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow