Agrupar expresiones Lambda por operadores y usarlas con DapperExtensions 'PredicateGroups

c# dapper extension-methods

Pregunta

De acuerdo con mi pregunta anterior: Pulling Apart Expression<Func<T, object>> - Estoy tratando de hacerlo un poco más avanzado. Actualmente, puedo hacer esto:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);

que se convertirá en un campo de FieldPredicate :

// Assume I've successfully parsed p => p.MarketId == marketId into its constituent parts:
// left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
Predicates.Field(left, theOperator, right);

Ahora quiero ser capaz de hacer esto:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack");

y generar SQL que se ve así:

DECLARE @MarketId INT = 3
DECLARE @FirstName01 VARCHAR(MAX) = 'John'
DECLARE @FirstName02 VARCHAR(MAX) = 'Jack'

SELECT *
FROM Person
WHERE MarketId = @MarketId AND (FirstName = @FirstName01 OR FirstName = @FirstName02)

mediante el uso de DapperExtensions Compound Predicate Groups :

// ** This is the code I am trying to dynamically create based on the lambda that is passed in **

var predicateGroupAnd = new PredicateGroup {Operator = GroupOperator.And, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupOr = new PredicateGroup {Operator = GroupOperator.Or, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "John"
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "Jack"
predicateGroupOr.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupAll = new PredicateGroup // This is what will be passed to DapperExtensions' GetList<T> method
    {
        Operator = GroupOperator.And, // How do I set this correctly?
        Predicates = new List<IPredicate> {predicateGroupAnd, predicateGroupOr}
    };

Mi problema parece estar en la forma en que se analizan los árboles de expresión. Supongamos que tenemos la expresión lambda:

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

Puedo convertir esto a BinaryExpression . Si uso BinaryExpression.Left , obtengo

p.MarketId == marketId && p.FirstName == "John"

y BinaryExpression.Right yields:

p.FirstName == "Jack"

Además, el NodeType de la BinaryExpression general parece estar configurado en el último operador condicional de la lambda, es decir, ExpressionType.OrElse

Siento que necesito usar recursividad y recorrer la expresión lambda de derecha a izquierda, pero no he podido crear los predicados de grupo compuesto que quiero. Específicamente, ¿cómo se agrupan las lambdas AND juntas, y las lambdas OR juntas? ¡Gracias!

Respuesta popular

Tu ejemplo lambda,

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

es el equivalente de

p => (p.MarketId == marketId && p.FirstName == "John") || p.FirstName == "Jack"

porque && tiene una precendencia mayor que || .

Debido a esto obtendrás un árbol con el && en el "fondo", (ya que primero se debe calcular), luego el || en la parte superior:

                         ||
                        /  \
                       &&  Firstname == "Jack"
                      /  \
p.MarketId == marketId    p.FirstName == "John"

Una vez que comprenda la precedencia del operador, lo anterior tiene sentido. Si quieres una alternativa, puedes usar corchetes para forzar || para ser evaluado primero (haciendo que termine en la parte inferior del árbol de expresiones).

p => p.MarketId == marketId && (p.FirstName == "John" || p.FirstName == "Jack")

Tu problema general es que te estás acercando a esto un poco mal. Actualmente intenta crear 2 grupos, uno para quirófanos y uno para AND. Esto podría funcionar en este caso, pero no en el general, por ejemplo, qué harías para esto: (a && b) || (c && d) ?

Creo que lo que debería suceder es que cada uno y cada uno debería traducirse en su propio grupo de predicados. Consulte la sección "Predicados compuestos múltiples (grupo de predicados)" de su artículo vinculado. Simplemente reemplazará BinaryExpression por un grupo de predicados.

En tu ejemplo, tienes (a && b) || c con el || en la cima. Con lo que desea terminar es cada vez que tiene un operador que desea crear un grupo de predicados, con la izquierda y derecha como la lista de expresiones. En su lógica para convertir una expresión binar a un grupo de predicados, primero usaría la misma función para convertir la izquierda y la derecha en un grupo de predicados

es decir, su código ve el ||.
crea un grupo de predicados listo para agregar las expresiones a su lista
elige la izquierda primero (no importa).
ok, left es otra expresión binaria, por lo que se llama a sí misma para obtener un predicategroup que su biblioteca de destino entiende
de modo que la recursividad solo funciona en a && b. Elige primero la izquierda, ve un predicado simple, a, lo que significa que simplemente puede agregarlo al grupo de predicados, hace lo mismo para la derecha
luego volvemos a la llamada original a la función que ahora tiene un grupo de predicados con la expresión más baja a la izquierda convertida en un grupo de predicados diferente y agregado. desciende ahora mismo, que es un predicado individual simple y puede agregarlo a su lista.

Está bien, entonces si tuviera algo loco como: a && b || c || d && e

Ok, teniendo en cuenta una precedencia más alta obtenemos lo siguiente: ((a && b) || c) || (d && e) tenga en cuenta que no estoy 100% seguro de que el corchete pondría c con el primer && o el último, pero no importa, la lógica es la misma Al visualizar el árbol, comience con el más interno soportes. Son las "hojas" que luego funcionan hacia afuera, usando los corchetes para trabajar el árbol, llegando finalmente al nodo raíz, que en nuestro caso es el || a la derecha de c:

       ||
     /    \
    ||     &&
   /  \   /  \
  &&   c d   e
 /  \
a    b


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué