コード例 #1
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));
        }
コード例 #2
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));
        }