Example #1
0
        private CodeExpression VisitBinaryExpression(QueryBinaryExpression expression, CodeBinaryOperatorType binaryOperator)
        {
            CodeExpression left  = this.GenerateCode(expression.Left);
            CodeExpression right = this.GenerateCode(expression.Right);

            return(new CodeBinaryOperatorExpression(left, binaryOperator, right));
        }
Example #2
0
            protected override Expression VisitBinaryExpression(QueryBinaryExpression expression, Func <Expression, Expression, BinaryExpression> builderMethodCall)
            {
                Expression left  = this.GenerateLinqExpression(expression.Left);
                Expression right = this.GenerateLinqExpression(expression.Right);

                if (Nullable.GetUnderlyingType(left.Type) != null)
                {
                    left = Expression.Property(left, "Value");
                }

                if (Nullable.GetUnderlyingType(right.Type) != null)
                {
                    right = Expression.Property(right, "Value");
                }

                var leftTypeCode  = Type.GetTypeCode(left.Type);
                var rightTypeCode = Type.GetTypeCode(right.Type);

                if (leftTypeCode > rightTypeCode)
                {
                    right = Expression.Convert(right, left.Type);
                }
                else if (rightTypeCode > leftTypeCode)
                {
                    left = Expression.Convert(left, right.Type);
                }

                return(builderMethodCall(left, right));
            }
            /// <summary>
            /// Visits a QueryExpression whose root node is the QueryBinaryToExpression..
            /// </summary>
            /// <param name="expression">The root node of the expression tree being visited.</param>
            /// <param name="uriFormat">The format of uri string.</param>
            /// <returns>Replaced expression.</returns>
            private string VisitBinaryExpession(QueryBinaryExpression expression, string uriFormat)
            {
                string left  = this.Convert(expression.Left);
                string right = this.Convert(expression.Right);

                return(string.Format(CultureInfo.InvariantCulture, uriFormat, left, right));
            }
        private QueryExpression VisitBinaryExpressionWithBooleanResult(QueryBinaryExpression expression, Func <QueryExpression, QueryExpression, QueryType, QueryExpression> builderFunc)
        {
            var left  = this.ResolveTypes(expression.Left);
            var right = this.ResolveTypes(expression.Right);
            var type  = this.EvaluationStrategy.BooleanType;

            return(builderFunc(left, right, type));
        }
        /// <summary>
        /// Resolves types for binary arithmetic expression.
        /// </summary>
        /// <param name="expression">The expression to resolve types for.</param>
        /// <param name="builderFunc">Func used to build the expression.</param>
        /// <returns>Expression with resolved types.</returns>
        protected QueryExpression VisitBinaryArithmeticExpression(QueryBinaryExpression expression, Func <QueryExpression, QueryExpression, QueryType, QueryExpression> builderFunc)
        {
            var left  = this.ResolveTypes(expression.Left);
            var right = this.ResolveTypes(expression.Right);
            var type  = this.EvaluationStrategy.GetCommonType((QueryScalarType)left.ExpressionType, (QueryScalarType)right.ExpressionType);

            type = this.EvaluationStrategy.RemoveLengthConstraints(type);

            return(builderFunc(left, right, type));
        }
        /// <summary>
        /// Handles a binary expression
        /// </summary>
        /// <param name="expression">The expression</param>
        /// <param name="commonQueryBuilderCall">The func to contruct the binary expression.</param>
        /// <returns>Replaced expression</returns>
        protected QueryExpression VisitBinaryExpression(
            QueryBinaryExpression expression,
            Func <QueryExpression, QueryExpression, QueryType, QueryExpression> commonQueryBuilderCall)
        {
            QueryExpression left  = this.ReplaceExpression(expression.Left);
            QueryExpression right = this.ReplaceExpression(expression.Right);

            if (this.HasChanged(expression.Left, left) || this.HasChanged(expression.Right, right))
            {
                return(commonQueryBuilderCall(left, right, expression.ExpressionType));
            }

            return(expression);
        }
Example #7
0
        protected void EvaluateBinaryArguments <TArgumentType>(QueryBinaryExpression expression, out TArgumentType left, out TArgumentType right)
            where TArgumentType : QueryValue
        {
            left  = null;
            right = null;

            left = this.Evaluate(expression.Left) as TArgumentType;
            if (left == null)
            {
                throw new TaupoInvalidOperationException("Left operand must be a " + typeof(TArgumentType).Name + ".");
            }

            right = this.Evaluate(expression.Right) as TArgumentType;
            if (right == null)
            {
                throw new TaupoInvalidOperationException("Right operand must be a " + typeof(TArgumentType).Name + ".");
            }
        }
Example #8
0
        /// <summary>
        ///     Get query properties
        /// </summary>
        /// <param name="expr">The expression.</param>
        /// <param name="linkingType">Type of the linking.</param>
        private QueryExpression GetQueryProperties(Expression expr, ExpressionType linkingType)
        {
            var isNotUnary = false;

            if (expr is UnaryExpression unaryExpression)
            {
                if (unaryExpression.NodeType == ExpressionType.Not && unaryExpression.Operand is MethodCallExpression)
                {
                    expr       = unaryExpression.Operand;
                    isNotUnary = true;
                }
            }

            if (expr is MethodCallExpression methodCallExpression)
            {
                var methodName = methodCallExpression.Method.Name;
                var exprObj    = methodCallExpression.Object;
MethodLabel:
                switch (methodName)
                {
                case "Contains":
                {
                    if (exprObj != null &&
                        exprObj.NodeType == ExpressionType.MemberAccess &&
                        exprObj.Type == typeof(string))
                    {
                        methodName = "StringContains";
                        goto MethodLabel;
                    }

                    var propertyName = ExpressionHelper.GetPropertyNamePath(methodCallExpression, out var isNested);

                    if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                        !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                    {
                        throw new NotSupportedException("predicate can't parse");
                    }

                    var propertyValue = ExpressionHelper.GetValuesFromCollection(methodCallExpression);
                    var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                    var link          = ExpressionHelper.GetSqlOperator(linkingType);
                    return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                }

                case "StringContains":
                case "CompareString":
                case "Equals":
                case "StartsWith":
                case "EndsWith":
                {
                    if (exprObj == null ||
                        exprObj.NodeType != ExpressionType.MemberAccess)
                    {
                        goto default;
                    }

                    var propertyName = ExpressionHelper.GetPropertyNamePath(exprObj, out bool isNested);

                    if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                        !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                    {
                        throw new NotSupportedException("predicate can't parse");
                    }

                    var propertyValue = ExpressionHelper.GetValuesFromStringMethod(methodCallExpression);
                    var likeValue     = ExpressionHelper.GetSqlLikeValue(methodName, propertyValue);
                    var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                    var link          = ExpressionHelper.GetSqlOperator(linkingType);
                    return(new QueryParameterExpression(link, propertyName, likeValue, opr, isNested));
                }

                default:
                    throw new NotSupportedException($"'{methodName}' method is not supported");
                }
            }

            if (expr is BinaryExpression binaryExpression)
            {
                if (binaryExpression.NodeType != ExpressionType.AndAlso && binaryExpression.NodeType != ExpressionType.OrElse)
                {
                    var  propertyName  = ExpressionHelper.GetPropertyNamePath(binaryExpression, out var isNested);
                    bool checkNullable = isNested && propertyName.EndsWith("HasValue");

                    if (!checkNullable)
                    {
                        if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                            !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                        {
                            throw new NotSupportedException("predicate can't parse");
                        }
                    }
                    else
                    {
                        var prop = SqlProperties.FirstOrDefault(x => x.IsNullable && x.PropertyName + "HasValue" == propertyName);
                        if (prop == null)
                        {
                            prop = SqlJoinProperties.FirstOrDefault(x => x.IsNullable && x.PropertyName + "HasValue" == propertyName);
                            if (prop == null)
                            {
                                throw new NotSupportedException("predicate can't parse");
                            }
                        }
                        else
                        {
                            isNested = false;
                        }

                        propertyName = prop.PropertyName;
                    }

                    var propertyValue = ExpressionHelper.GetValue(binaryExpression.Right);
                    var nodeType      = checkNullable ? ((bool)propertyValue == false ? ExpressionType.Equal : ExpressionType.NotEqual) : binaryExpression.NodeType;
                    if (checkNullable)
                    {
                        propertyValue = null;
                    }
                    var opr  = ExpressionHelper.GetSqlOperator(nodeType);
                    var link = ExpressionHelper.GetSqlOperator(linkingType);

                    return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                }

                var leftExpr  = GetQueryProperties(binaryExpression.Left, ExpressionType.Default);
                var rightExpr = GetQueryProperties(binaryExpression.Right, binaryExpression.NodeType);

                switch (leftExpr)
                {
                case QueryParameterExpression lQPExpr:
                    if (!string.IsNullOrEmpty(lQPExpr.LinkingOperator) && !string.IsNullOrEmpty(rightExpr.LinkingOperator))     // AND a AND B
                    {
                        switch (rightExpr)
                        {
                        case QueryBinaryExpression rQBExpr:
                            if (lQPExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)         // AND a AND (c AND d)
                            {
                                var nodes = new QueryBinaryExpression
                                {
                                    LinkingOperator = leftExpr.LinkingOperator,
                                    Nodes           = new List <QueryExpression> {
                                        leftExpr
                                    }
                                };

                                rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                nodes.Nodes.AddRange(rQBExpr.Nodes);

                                leftExpr  = nodes;
                                rightExpr = null;
                                // AND a AND (c AND d) => (AND a AND c AND d)
                            }

                            break;
                        }
                    }

                    break;

                case QueryBinaryExpression lQBExpr:
                    switch (rightExpr)
                    {
                    case QueryParameterExpression rQPExpr:
                        if (rQPExpr.LinkingOperator == lQBExpr.Nodes.Last().LinkingOperator)         //(a AND b) AND c
                        {
                            lQBExpr.Nodes.Add(rQPExpr);
                            rightExpr = null;
                            //(a AND b) AND c => (a AND b AND c)
                        }

                        break;

                    case QueryBinaryExpression rQBExpr:
                        if (lQBExpr.Nodes.Last().LinkingOperator == rQBExpr.LinkingOperator)         // (a AND b) AND (c AND d)
                        {
                            if (rQBExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)     // AND (c AND d)
                            {
                                rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                lQBExpr.Nodes.AddRange(rQBExpr.Nodes);
                                // (a AND b) AND (c AND d) =>  (a AND b AND c AND d)
                            }
                            else
                            {
                                lQBExpr.Nodes.Add(rQBExpr);
                                // (a AND b) AND (c OR d) =>  (a AND b AND (c OR d))
                            }

                            rightExpr = null;
                        }

                        break;
                    }

                    break;
                }

                var nLinkingOperator = ExpressionHelper.GetSqlOperator(linkingType);
                if (rightExpr == null)
                {
                    leftExpr.LinkingOperator = nLinkingOperator;
                    return(leftExpr);
                }

                return(new QueryBinaryExpression
                {
                    NodeType = QueryExpressionType.Binary,
                    LinkingOperator = nLinkingOperator,
                    Nodes = new List <QueryExpression> {
                        leftExpr, rightExpr
                    },
                });
            }

            return(GetQueryProperties(ExpressionHelper.GetBinaryExpression(expr), linkingType));
        }
Example #9
0
        /// <summary>
        ///     Get query properties
        /// </summary>
        /// <param name="expr">The expression.</param>
        /// <param name="linkingType">Type of the linking.</param>
        private QueryExpression GetQueryProperties(Expression expr, ExpressionType linkingType)
        {
            var isNotUnary = false;

            if (expr is UnaryExpression unaryExpression)
            {
                if (unaryExpression.NodeType == ExpressionType.Not && unaryExpression.Operand is MethodCallExpression)
                {
                    expr       = unaryExpression.Operand;
                    isNotUnary = true;
                }
            }

            if (expr is MethodCallExpression methodCallExpression)
            {
                var methodName = methodCallExpression.Method.Name;
                var exprObj    = methodCallExpression.Object;
MethodLabel:
                switch (methodName)
                {
                case "Contains":
                {
                    if (exprObj != null &&
                        exprObj.NodeType == ExpressionType.MemberAccess &&
                        exprObj.Type == typeof(string))
                    {
                        methodName = "StringContains";
                        goto MethodLabel;
                    }

                    var propertyName = ExpressionHelper.GetPropertyNamePath(methodCallExpression, out var isNested);

                    if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                        !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                    {
                        throw new NotSupportedException("predicate can't parse");
                    }

                    var propertyValue = ExpressionHelper.GetValuesFromCollection(methodCallExpression);
                    var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                    var link          = ExpressionHelper.GetSqlOperator(linkingType);

                    // Handle empty list passed scenario
                    // https://github.com/StackExchange/Dapper/issues/565
                    if (opr.ToUpper().Contains("IN") && propertyValue is System.Collections.ICollection collection && collection.Count == 0)
                    {
                        propertyValue = new[] { "" }
                    }
                    ;

                    return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                }

                case "StringContains":
                case "StartsWith":
                case "EndsWith":
                {
                    if (exprObj == null ||
                        exprObj.NodeType != ExpressionType.MemberAccess ||
                        exprObj.Type != typeof(string))
                    {
                        goto default;
                    }

                    var propertyName = ExpressionHelper.GetPropertyNamePath(exprObj, out bool isNested);

                    if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                        !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                    {
                        throw new NotSupportedException("predicate can't parse");
                    }

                    var propertyValue = ExpressionHelper.GetValuesFromStringMethod(methodCallExpression);
                    var likeValue     = ExpressionHelper.GetSqlLikeValue(methodName, propertyValue);
                    var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                    var link          = ExpressionHelper.GetSqlOperator(linkingType);
                    return(new QueryParameterExpression(link, propertyName, likeValue, opr, isNested));
                }

                default:
                    throw new NotSupportedException($"'{methodName}' method is not supported");
                }
            }

            if (expr is BinaryExpression binaryExpression)
            {
                if (binaryExpression.NodeType != ExpressionType.AndAlso && binaryExpression.NodeType != ExpressionType.OrElse)
                {
                    var propertyName = ExpressionHelper.GetPropertyNamePath(binaryExpression, out var isNested);

                    if (!SqlProperties.Select(x => x.PropertyName).Contains(propertyName) &&
                        !SqlJoinProperties.Select(x => x.PropertyName).Contains(propertyName))
                    {
                        throw new NotSupportedException("predicate can't parse");
                    }

                    var propertyValue = ExpressionHelper.GetValue(binaryExpression.Right);
                    var opr           = ExpressionHelper.GetSqlOperator(binaryExpression.NodeType);
                    var link          = ExpressionHelper.GetSqlOperator(linkingType);

                    return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                }

                var leftExpr  = GetQueryProperties(binaryExpression.Left, ExpressionType.Default);
                var rightExpr = GetQueryProperties(binaryExpression.Right, binaryExpression.NodeType);

                switch (leftExpr)
                {
                case QueryParameterExpression lQPExpr:
                    if (!string.IsNullOrEmpty(lQPExpr.LinkingOperator) && !string.IsNullOrEmpty(rightExpr.LinkingOperator))     // AND a AND B
                    {
                        switch (rightExpr)
                        {
                        case QueryBinaryExpression rQBExpr:
                            if (lQPExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)         // AND a AND (c AND d)
                            {
                                var nodes = new QueryBinaryExpression
                                {
                                    LinkingOperator = leftExpr.LinkingOperator,
                                    Nodes           = new List <QueryExpression> {
                                        leftExpr
                                    }
                                };

                                rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                nodes.Nodes.AddRange(rQBExpr.Nodes);

                                leftExpr  = nodes;
                                rightExpr = null;
                                // AND a AND (c AND d) => (AND a AND c AND d)
                            }
                            break;
                        }
                    }
                    break;

                case QueryBinaryExpression lQBExpr:
                    switch (rightExpr)
                    {
                    case QueryParameterExpression rQPExpr:
                        if (rQPExpr.LinkingOperator == lQBExpr.Nodes.Last().LinkingOperator)            //(a AND b) AND c
                        {
                            lQBExpr.Nodes.Add(rQPExpr);
                            rightExpr = null;
                            //(a AND b) AND c => (a AND b AND c)
                        }
                        break;

                    case QueryBinaryExpression rQBExpr:
                        if (lQBExpr.Nodes.Last().LinkingOperator == rQBExpr.LinkingOperator)         // (a AND b) AND (c AND d)
                        {
                            if (rQBExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)     // AND (c AND d)
                            {
                                rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                lQBExpr.Nodes.AddRange(rQBExpr.Nodes);
                                // (a AND b) AND (c AND d) =>  (a AND b AND c AND d)
                            }
                            else
                            {
                                lQBExpr.Nodes.Add(rQBExpr);
                                // (a AND b) AND (c OR d) =>  (a AND b AND (c OR d))
                            }
                            rightExpr = null;
                        }
                        break;
                    }
                    break;
                }

                var nLinkingOperator = ExpressionHelper.GetSqlOperator(linkingType);
                if (rightExpr == null)
                {
                    leftExpr.LinkingOperator = nLinkingOperator;
                    return(leftExpr);
                }

                return(new QueryBinaryExpression
                {
                    NodeType = QueryExpressionType.Binary,
                    LinkingOperator = nLinkingOperator,
                    Nodes = new List <QueryExpression> {
                        leftExpr, rightExpr
                    },
                });
            }

            return(GetQueryProperties(ExpressionHelper.GetBinaryExpression(expr), linkingType));
        }
Example #10
0
            /// <summary>
            /// get query properties
            /// </summary>
            /// <param name="expr">The expression.</param>
            /// <param name="linkingType">Type of the linking.</param>
            private QueryExpression GetQueryProperties(Expression expr, ExpressionType linkingType)
            {
                #region 适配一元 NOT 运算符

                var isNotUnary = false;
                if (expr is UnaryExpression)
                {
                    var innerbody = (UnaryExpression)expr;
                    if (innerbody.NodeType == ExpressionType.Not && innerbody.Operand is MethodCallExpression)
                    {
                        expr       = innerbody.Operand;
                        isNotUnary = true;
                    }
                }

                #endregion

                var body = expr as MethodCallExpression;
                if (body != null)
                {
                    var innerBody  = body;
                    var methodName = innerBody.Method.Name;
MethodLabel:
                    switch (methodName)
                    {
                    case "Contains":
                    {
                        if (innerBody.Object != null &&
                            innerBody.Object.NodeType == ExpressionType.MemberAccess &&
                            innerBody.Object.Type == typeof(string))
                        {
                            methodName = "StringContains";
                            goto MethodLabel;
                        }

                        bool isNested     = false;
                        var  propertyName = ExpressionHelper.GetPropertyNamePath(innerBody, out isNested);

                        if (!PropertieNames.Contains(propertyName))
                        {
                            throw new NotImplementedException("predicate can't parse");
                        }

                        var propertyValue = ExpressionHelper.GetValuesFromCollection(innerBody);
                        var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                        var link          = ExpressionHelper.GetSqlOperator(linkingType);
                        return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                    }

                    case "StringContains":
                    case "StartsWith":
                    case "EndsWith":
                    {
                        if (innerBody.Object == null ||
                            innerBody.Object.NodeType != ExpressionType.MemberAccess ||
                            innerBody.Object.Type != typeof(string))
                        {
                            goto default;
                        }

                        bool isNested     = false;
                        var  propertyName = ExpressionHelper.GetPropertyNamePath(innerBody.Object, out isNested);

                        if (!PropertieNames.Contains(propertyName))
                        {
                            throw new NotImplementedException("wherePredicate can't parse");
                        }

                        var propertyValue = ExpressionHelper.GetValuesFromStringMethod(innerBody);
                        var likeValue     = ExpressionHelper.GetSqlLikeValue(methodName, propertyValue);
                        var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                        var link          = ExpressionHelper.GetSqlOperator(linkingType);
                        return(new QueryParameterExpression(link, propertyName, likeValue, opr, isNested));
                    }

                    case "SqlLike":
                    case "Like":
                    {
                        bool isNested     = false;
                        var  propertyName = ExpressionHelper.GetPropertyNamePath(innerBody, out isNested);

                        if (!PropertieNames.Contains(propertyName))
                        {
                            throw new NotImplementedException("wherePredicate can't parse");
                        }

                        var propertyValue = ExpressionHelper.GetValuesFromStringMethod(innerBody);
                        var opr           = ExpressionHelper.GetMethodCallSqlOperator(methodName, isNotUnary);
                        var link          = ExpressionHelper.GetSqlOperator(linkingType);
                        return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                    }

                    default:
                        throw new NotImplementedException($"'{methodName}' method is not implemented");
                    }
                }
                else if (expr is BinaryExpression)
                {
                    var innerbody = (BinaryExpression)expr;
                    if (innerbody.NodeType != ExpressionType.AndAlso && innerbody.NodeType != ExpressionType.OrElse)
                    {
                        bool isNested     = false;
                        var  propertyName = ExpressionHelper.GetPropertyNamePath(innerbody, out isNested);

                        if (!PropertieNames.Contains(propertyName))
                        {
                            throw new NotImplementedException("predicate can't parse");
                        }

                        var propertyValue = ExpressionHelper.GetValue(innerbody.Right);
                        var opr           = ExpressionHelper.GetSqlOperator(innerbody.NodeType);
                        var link          = ExpressionHelper.GetSqlOperator(linkingType);

                        return(new QueryParameterExpression(link, propertyName, propertyValue, opr, isNested));
                    }
                    else
                    {
                        var leftExpr  = GetQueryProperties(innerbody.Left, ExpressionType.Default);
                        var rightExpr = GetQueryProperties(innerbody.Right, innerbody.NodeType);

                        #region 剥离层级分组括号

                        switch (leftExpr)
                        {
                        case QueryParameterExpression lQPExpr:
                            if (!string.IsNullOrEmpty(lQPExpr.LinkingOperator) && !string.IsNullOrEmpty(rightExpr.LinkingOperator))     // AND a AND B
                            {
                                switch (rightExpr)
                                {
                                case QueryBinaryExpression rQBExpr:
                                    if (lQPExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)         // AND a AND (c AND d)
                                    {
                                        var nodes = new QueryBinaryExpression
                                        {
                                            LinkingOperator = leftExpr.LinkingOperator,
                                            Nodes           = new List <QueryExpression> {
                                                leftExpr
                                            }
                                        };

                                        rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                        nodes.Nodes.AddRange(rQBExpr.Nodes);

                                        leftExpr  = nodes;
                                        rightExpr = null;
                                        // AND a AND (c AND d) => (AND a AND c AND d)
                                    }
                                    break;
                                }
                            }
                            break;

                        case QueryBinaryExpression lQBExpr:
                            switch (rightExpr)
                            {
                            case QueryParameterExpression rQPExpr:
                                if (rQPExpr.LinkingOperator == lQBExpr.Nodes.Last().LinkingOperator)            //(a AND b) AND c
                                {
                                    lQBExpr.Nodes.Add(rQPExpr);
                                    rightExpr = null;
                                    //(a AND b) AND c => (a AND b AND c)
                                }
                                break;

                            case QueryBinaryExpression rQBExpr:
                                if (lQBExpr.Nodes.Last().LinkingOperator == rQBExpr.LinkingOperator)         // (a AND b) AND (c AND d)
                                {
                                    if (rQBExpr.LinkingOperator == rQBExpr.Nodes.Last().LinkingOperator)     // AND (c AND d)
                                    {
                                        rQBExpr.Nodes[0].LinkingOperator = rQBExpr.LinkingOperator;
                                        lQBExpr.Nodes.AddRange(rQBExpr.Nodes);
                                        // (a AND b) AND (c AND d) =>  (a AND b AND c AND d)
                                    }
                                    else
                                    {
                                        lQBExpr.Nodes.Add(rQBExpr);
                                        // (a AND b) AND (c OR d) =>  (a AND b AND (c OR d))
                                    }
                                    rightExpr = null;
                                }
                                break;
                            }
                            break;
                        }

                        #endregion

                        var nLinkingOperator = ExpressionHelper.GetSqlOperator(linkingType);
                        if (rightExpr == null)
                        {
                            leftExpr.LinkingOperator = nLinkingOperator;
                            return(leftExpr);
                        }

                        return(new QueryBinaryExpression
                        {
                            NodeType = QueryExpressionType.Binary,
                            LinkingOperator = nLinkingOperator,
                            Nodes = new List <QueryExpression> {
                                leftExpr, rightExpr
                            },
                        });
                    }
                }
                else
                {
                    return(GetQueryProperties(ExpressionHelper.GetBinaryExpression(expr), linkingType));
                }
            }