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)); }
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); }
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 + "."); } }
/// <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)); }
/// <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)); }
/// <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)); } }