예제 #1
0
        /// <summary>Makes the expression that is used as a filter corresponding to skip token.</summary>
        /// <param name="topLevelOrderingInfo">Ordering expression.</param>
        /// <param name="skipToken">The provided skip token.</param>
        /// <param name="parameterType">The parameter type of the lambda.</param>
        /// <returns>LambdaExpression corresponding to the skip token filter.</returns>
        internal LambdaExpression BuildSkipTokenFilter(OrderingInfo topLevelOrderingInfo, IList <object> skipToken, Type parameterType)
        {
            ParameterExpression element       = Expression.Parameter(parameterType, "element");
            Expression          lastCondition = Expression.Constant(true, typeof(bool));
            Expression          lastMatch     = Expression.Constant(false, typeof(bool));

            foreach (var v in WebUtil.Zip(topLevelOrderingInfo.OrderingExpressions, skipToken, (x, y) => new { Order = x, Value = y }))
            {
                BinaryOperatorKind comparisonExp = v.Order.IsAscending ? BinaryOperatorKind.GreaterThan : BinaryOperatorKind.LessThan;

                Expression fixedLambda = ParameterReplacerVisitor.Replace(
                    ((LambdaExpression)v.Order.Expression).Body,
                    ((LambdaExpression)v.Order.Expression).Parameters[0],
                    element);

                // TODO: this will be an EnumNode if $skiptoken contains enum constant.
                ConstantNode node;
                var          lexer   = new ExpressionLexer((string)v.Value);
                bool         success = TokenToQueryNodeTranslator.TryCreateLiteral(lexer.CurrentToken, out node);
                Debug.Assert(success, "Was not a literal");

                node = this.EnsureCorrectTypeAndPrecisionForLFDM(node, fixedLambda.Type);
                Expression right      = this.nodeToExpressionTranslator.TranslateNode(node);
                Expression comparison = ExpressionGenerator.GenerateLogicalAnd(
                    lastCondition,
                    this.GenerateNullAwareComparison(fixedLambda, right, comparisonExp));

                lastMatch = ExpressionGenerator.GenerateLogicalOr(lastMatch, comparison);

                lastCondition = ExpressionGenerator.GenerateLogicalAnd(
                    lastCondition,
                    this.GenerateComparisonExpression(fixedLambda, right, BinaryOperatorKind.Equal));
            }

            lastMatch = ExpressionUtils.EnsurePredicateExpressionIsBoolean(lastMatch);

            Debug.Assert(lastMatch.Type == typeof(bool), "Skip token should generate boolean expression.");

            return(Expression.Lambda(lastMatch, element));
        }
예제 #2
0
        /// <summary>
        /// Generates a comparison expression which can handle NULL values for any type.
        /// NULL is always treated as the smallest possible value.
        /// So for example for strings NULL is smaller than any non-NULL string.
        /// For now only GreaterThan and LessThan operators are supported by this method.
        /// </summary>
        /// <param name="left">Left hand side expression</param>
        /// <param name="right">Right hand side expression</param>
        /// <param name="operatorKind">gt or lt operator token</param>
        /// <returns>Resulting comparison expression (has a Boolean value)</returns>
        private Expression GenerateNullAwareComparison(Expression left, Expression right, BinaryOperatorKind operatorKind)
        {
            Debug.Assert(
                operatorKind == BinaryOperatorKind.GreaterThan || operatorKind == BinaryOperatorKind.LessThan,
                "Only GreaterThan or LessThan operators are supported by the GenerateNullAwateComparison method for now.");

            if (WebUtil.TypeAllowsNull(left.Type))
            {
                if (!WebUtil.TypeAllowsNull(right.Type))
                {
                    right = Expression.Convert(right, typeof(Nullable <>).MakeGenericType(right.Type));
                }
            }
            else if (WebUtil.TypeAllowsNull(right.Type))
            {
                left = Expression.Convert(left, typeof(Nullable <>).MakeGenericType(left.Type));
            }
            else
            {
                // Can't perform NULL aware comparison on this type. Just let the normal
                // comparison deal with it. Since the type doesn't allow NULL one should
                // never appear, so normal comparison is just fine.
                return(this.GenerateComparisonExpression(left, right, operatorKind));
            }

            switch (operatorKind)
            {
            case BinaryOperatorKind.GreaterThan:
                // (left != null) && ((right == null) || Compare(left, right) > 0)
                if (left == ExpressionUtils.NullLiteral)
                {
                    return(Expression.Constant(false, typeof(bool)));
                }
                else if (right == ExpressionUtils.NullLiteral)
                {
                    return(ExpressionGenerator.GenerateNotEqual(left, Expression.Constant(null, left.Type)));
                }
                else
                {
                    return(ExpressionGenerator.GenerateLogicalAnd(
                               ExpressionGenerator.GenerateNotEqual(left, Expression.Constant(null, left.Type)),
                               ExpressionGenerator.GenerateLogicalOr(
                                   ExpressionGenerator.GenerateEqual(right, Expression.Constant(null, right.Type)),
                                   this.GenerateComparisonExpression(left, right, operatorKind))));
                }

            case BinaryOperatorKind.LessThan:
                // (right != null) && ((left == null) || Compare(left, right) < 0)
                if (right == ExpressionUtils.NullLiteral)
                {
                    return(Expression.Constant(false, typeof(bool)));
                }
                else if (left == ExpressionUtils.NullLiteral)
                {
                    return(ExpressionGenerator.GenerateNotEqual(right, Expression.Constant(null, right.Type)));
                }
                else
                {
                    return(ExpressionGenerator.GenerateLogicalAnd(
                               ExpressionGenerator.GenerateNotEqual(right, Expression.Constant(null, left.Type)),
                               ExpressionGenerator.GenerateLogicalOr(
                                   ExpressionGenerator.GenerateEqual(left, Expression.Constant(null, right.Type)),
                                   this.GenerateComparisonExpression(left, right, operatorKind))));
                }

            default:
                // For now only < and > are supported as we use this only from $skiptoken
                string message = ServiceStrings.RequestQueryParser_NullOperatorUnsupported(operatorKind);
                throw DataServiceException.CreateSyntaxError(message);
            }
        }