diff --git a/umbraco.MacroEngines.Juno/DynamicQueryable.cs b/umbraco.MacroEngines.Juno/DynamicQueryable.cs index 15d176a624..72b5021d2d 100644 --- a/umbraco.MacroEngines.Juno/DynamicQueryable.cs +++ b/umbraco.MacroEngines.Juno/DynamicQueryable.cs @@ -780,7 +780,7 @@ namespace System.Linq.Dynamic NextToken(); Expression right = ParseLogicalAnd(); CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); - left = Expression.OrElse(left, right); + left = HandleDynamicNodeLambdas(ExpressionType.OrElse, left, right); } return left; } @@ -795,7 +795,7 @@ namespace System.Linq.Dynamic NextToken(); Expression right = ParseComparison(); CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos); - left = Expression.AndAlso(left, right); + left = HandleDynamicNodeLambdas(ExpressionType.AndAlso, left, right); } return left; } @@ -826,6 +826,10 @@ namespace System.Linq.Dynamic { left = Expression.Convert(left, right.Type); } + else if (left is LambdaExpression || right is LambdaExpression) + { + //do nothing here (but further down we'll handle the lambdaexpression) + } else { throw IncompatibleOperandsError(op.text, left, right, op.pos); @@ -1969,12 +1973,16 @@ namespace System.Linq.Dynamic Expression innerLeft = null; Expression innerRight = null; ParameterExpression[] parameters = null; + bool bothLambdas = false; + Type usualLambdaExpressionT = typeof(Func); if (left is LambdaExpression - && + && ( (typeof(Func).IsAssignableFrom(((LambdaExpression)left).Type)) || (typeof(Func).IsAssignableFrom(((LambdaExpression)left).Type)) + || + (typeof(Func).IsAssignableFrom(((LambdaExpression)left).Type)) )) { LambdaExpression leftLambda = (LambdaExpression)left; @@ -1988,6 +1996,7 @@ namespace System.Linq.Dynamic if (right is LambdaExpression) { //If the left hand side is also lambda, we'll have to use a switching expression tree for the conversion + //handled in the check for right, because it occurs second } } parameters = new ParameterExpression[leftLambda.Parameters.Count]; @@ -1999,6 +2008,8 @@ namespace System.Linq.Dynamic (typeof(Func).IsAssignableFrom(((LambdaExpression)right).Type)) || (typeof(Func).IsAssignableFrom(((LambdaExpression)right).Type)) + || + (typeof(Func).IsAssignableFrom(((LambdaExpression)right).Type)) )) { LambdaExpression rightLambda = (LambdaExpression)right; @@ -2006,16 +2017,46 @@ namespace System.Linq.Dynamic if (left is ConstantExpression) { innerRight = Expression.Convert(invokedExpr, (left as ConstantExpression).Type); + parameters = new ParameterExpression[rightLambda.Parameters.Count]; + rightLambda.Parameters.CopyTo(parameters, 0); } else { if (left is LambdaExpression) { - //If the left hand side is also lambda, we'll have to use a switching expression tree for the conversion + //both are lambda expressions + if (((LambdaExpression)left).Type.IsAssignableFrom(((LambdaExpression)right).Type)) + { + //if both are Func or Func or Func + //get the TOut from the Func + Type leftType = ((LambdaExpression)left).Type; + Type rightType = ((LambdaExpression)right).Type; + Type[] leftTypeGenericArguments = leftType.GetGenericArguments(); + Type[] rightTypeGenericArguments = rightType.GetGenericArguments(); + if (leftTypeGenericArguments.SequenceEqual(rightTypeGenericArguments)) + { + //both Lambdas should reduce down the same + if (leftTypeGenericArguments.Length == 2) + { + //get TOut (debugging - should be bool) + Type TOut = leftTypeGenericArguments[1]; + + if (expressionType == ExpressionType.AndAlso) + { + return PredicateBuilder.And(left as Expression>, right as Expression>); + } + if (expressionType == ExpressionType.OrElse) + { + return PredicateBuilder.Or(left as Expression>, right as Expression>); + } + + bothLambdas = true; + } + } + } } } - parameters = new ParameterExpression[rightLambda.Parameters.Count]; - rightLambda.Parameters.CopyTo(parameters, 0); + } //For some reason, this update doesn't actually update the expression even when left/right are clearly different @@ -2038,6 +2079,28 @@ namespace System.Linq.Dynamic return (Expression.Lambda>(Expression.LessThanOrEqual(innerLeft ?? left, innerRight ?? right), parameters)); case ExpressionType.Modulo: return (Expression.Lambda>(Expression.Modulo(innerLeft ?? left, innerRight ?? right), parameters)); + case ExpressionType.AndAlso: + if (bothLambdas) + { + return Expression.Equal(left, right); + //return PredicateBuilder.And(innerLeft as Expression>, innerRight as Expression>); + } + else + { + return (Expression.Lambda>(Expression.AndAlso(innerLeft ?? left, innerRight ?? right), parameters)); + } + //break; + case ExpressionType.OrElse: + if (bothLambdas) + { + return Expression.Equal(left, right); + //return PredicateBuilder.Or(innerLeft as Expression>, innerRight as Expression>); + } + else + { + return (Expression.Lambda>(Expression.OrElse(innerLeft ?? left, innerRight ?? right), parameters)); + } + //break; default: return Expression.Equal(left, right); } @@ -2442,4 +2505,26 @@ namespace System.Linq.Dynamic public const string CloseBracketOrCommaExpected = "']' or ',' expected"; public const string IdentifierExpected = "Identifier expected"; } + + public static class PredicateBuilder + { + public static Expression> True() { return f => true; } + public static Expression> False() { return f => false; } + + public static Expression> Or(this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); + return Expression.Lambda> + (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters); + } + + public static Expression> And(this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); + return Expression.Lambda> + (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); + } + } }