Esempio n. 1
0
        /// <summary>Sorts a query like a SQL ORDER BY clause does.</summary>
        /// <param name="source">Original source for query.</param>
        /// <param name="orderingInfo">Ordering definition to compose.</param>
        /// <returns>The composed query.</returns>
        internal static Expression OrderBy(Expression source, OrderingInfo orderingInfo)
        {
            Debug.Assert(source != null, "source != null");
            Debug.Assert(orderingInfo != null, "orderingInfo != null");

            Expression queryExpr  = source;
            bool       useOrderBy = true;

            foreach (OrderingExpression o in orderingInfo.OrderingExpressions)
            {
                LambdaExpression selectorLambda = (LambdaExpression)o.Expression;

                Type selectorType = selectorLambda.Body.Type;
                Debug.Assert(selectorType != null, "type != null");

                // ensure either the expression type is orderable (ie, primitive) or its an open expression.
                if (!WebUtil.IsPrimitiveType(selectorType) && !OpenTypeMethods.IsOpenExpression(selectorLambda.Body))
                {
                    throw DataServiceException.CreateBadRequestError(Strings.RequestQueryParser_OrderByDoesNotSupportType(WebUtil.GetTypeName(selectorType)));
                }

                if (useOrderBy)
                {
                    queryExpr = o.IsAscending ? queryExpr.QueryableOrderBy(selectorLambda) : queryExpr.QueryableOrderByDescending(selectorLambda);
                }
                else
                {
                    queryExpr = o.IsAscending ? queryExpr.QueryableThenBy(selectorLambda) : queryExpr.QueryableThenByDescending(selectorLambda);
                }

                useOrderBy = false;
            }

            return(queryExpr);
        }
Esempio n. 2
0
        /// <summary>Parse one of the literals of skip token.</summary>
        /// <param name="literal">Input literal.</param>
        /// <returns>Object resulting from conversion of literal.</returns>
        internal static object ParseSkipTokenLiteral(string literal)
        {
            ExpressionLexer l = new ExpressionLexer(literal);
            ConstantNode    node;

            if (!TokenToQueryNodeTranslator.TryCreateLiteral(l.CurrentToken, out node))
            {
                throw new InvalidOperationException(ServiceStrings.RequsetQueryParser_ExpectingLiteralInSkipToken(literal));
            }

            return(node.Value);
        }
        /// <summary>
        /// Parses the given token into a constant node of the given target type.
        /// </summary>
        /// <param name="targetType">The tarket type.</param>
        /// <param name="targetTypeName">The target type name.</param>
        /// <param name="token">The token to parse.</param>
        /// <returns>The parsed constant node.</returns>
        private static ConstantNode ParseTypedLiteral(Type targetType, string targetTypeName, ExpressionToken token)
        {
            object literalValue;

            if (!LiteralParser.ForExpressions.TryParseLiteral(targetType, token.Text, out literalValue))
            {
                string message = Strings.RequestQueryParser_UnrecognizedLiteral(targetTypeName, token.Text);
                throw DataServiceException.CreateSyntaxError(message);
            }

            return(new ConstantNode(literalValue, token.Text));
        }
        /// <summary>
        /// Finds the entity set that this navigation property refers to.
        /// </summary>
        /// <param name="navigationProperty">Instance of navigation property.</param>
        /// <returns>an instance of IEdmEntitySet that this navigation property refers to.</returns>
        public IEdmNavigationSource FindNavigationTarget(IEdmNavigationProperty navigationProperty)
        {
            WebUtil.CheckArgumentNull(navigationProperty, "navigationProperty");
            MetadataProviderEdmEntitySet targetEntitySet = null;

            if (this.navigationTargetMapping == null || !this.navigationTargetMapping.TryGetValue(navigationProperty, out targetEntitySet))
            {
                string           declaringTypeName          = navigationProperty.DeclaringEntityType().FullName();
                ResourceType     declaringResourceType      = this.model.MetadataProvider.TryResolveResourceType(declaringTypeName);
                ResourceProperty navigationResourceProperty = declaringResourceType.TryResolvePropertiesDeclaredOnThisTypeByName(navigationProperty.Name);

                if (navigationResourceProperty != null && navigationResourceProperty.TypeKind == ResourceTypeKind.EntityType)
                {
                    // Calling this method causes the model to load all the metadata for the given association.
                    // Hence we do not need to add this to the target mapping explicitly
                    this.model.PairUpNavigationProperty(this.resourceSet, declaringResourceType, navigationResourceProperty);

                    // Since the entity set or target entity set might be hidden, the navigation target might not get added
                    // from the previous call
                    if (this.navigationTargetMapping != null)
                    {
                        this.navigationTargetMapping.TryGetValue(navigationProperty, out targetEntitySet);
                    }
                }
            }

            if (this.model.Mode == MetadataProviderEdmModelMode.SelectAndExpandParsing)
            {
                // When parsing $select/$expand, the URI parser has no way of knowing which sets are visible. So, if a navigation property is found
                // that does not have a target entity set, then it means that the target set is hidden. To avoid disclosing the existence of the set, act
                // as if the property does not exist.
                if (targetEntitySet == null)
                {
                    // We're playing a dangerous game here. We are trying to throw the SAME ERROR MESSAGE that the Uri Parser would throw
                    // for some property that is not found. If it looks any different, a client could detect the difference and learn about
                    // the existance of a navigation property that should have been hidden.
                    throw DataServiceException.CreateBadRequestError(Strings.BadRequest_InvalidPropertyNameSpecified(navigationProperty.Name, navigationProperty.DeclaringEntityType().FullName()));
                }
            }

            if (this.model.Mode == MetadataProviderEdmModelMode.UriPathParsing)
            {
                // As with select and expand, when parsing the path, the URI parser has no way of knowing which sets are visible. See above.
                if (targetEntitySet == null)
                {
                    // Same fragility as above.
                    throw DataServiceException.CreateResourceNotFound(navigationProperty.Name);
                }
            }

            return(targetEntitySet);
        }
        /// <summary>
        /// Validates if a service action is advertisable.
        /// </summary>
        /// <param name="resourceType">Resource type to which the service action is bound to.</param>
        /// <param name="serviceAction">Service action to be validated for advertisement.</param>
        /// <param name="existingOperations">The current set of actions. Used to avoid duplicate actions.</param>
        /// <returns>Validated service operation to be advertised. Null, if the service operation is not suppose to be advertised.</returns>
        private OperationWrapper ValidateCanAdvertiseServiceAction(ResourceType resourceType, ServiceAction serviceAction, OperationCache existingOperations)
        {
            Debug.Assert(resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType");

            if (serviceAction == null)
            {
                return(null);
            }

            Debug.Assert(!String.IsNullOrEmpty(serviceAction.Name), "The name of the service operation was null or empty");

            if (existingOperations.Contains(serviceAction))
            {
                throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_DuplicateAction(serviceAction.Name));
            }

            ServiceActionParameter bindingParameter = (ServiceActionParameter)serviceAction.BindingParameter;

            if (bindingParameter == null)
            {
                Debug.Assert(!String.IsNullOrEmpty(serviceAction.Name), "The name of the service action was null or empty");
                throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_ServiceActionBindingParameterNull(serviceAction.Name));
            }

            ResourceType bindingParameterType = bindingParameter.ParameterType;

            Debug.Assert(bindingParameterType != null, "bindingParameterType != null");

            // We only support advertising actions for entities and not entity collections. Since resourceType must be an entity type,
            // IsAssignableFrom will fail when the bindingParameterType is an entity collection type.
            if (!bindingParameterType.IsAssignableFrom(resourceType))
            {
                throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_ResourceTypeMustBeAssignableToBindingParameterResourceType(serviceAction.Name, bindingParameterType.FullName, resourceType.FullName));
            }

            Debug.Assert(bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityType, "We only support advertising actions for entities and not entity collections.");
            OperationWrapper operationWrapper = this.provider.ValidateOperation(serviceAction);

            if (operationWrapper != null)
            {
                existingOperations.Add(operationWrapper);
            }

            return(operationWrapper);
        }
Esempio n. 6
0
        /// <summary>Generates a not of expression.</summary>
        /// <param name="expr">Input expression.</param>
        /// <returns>The generated expression.</returns>
        internal static Expression GenerateNot(Expression expr)
        {
            if (OpenTypeMethods.IsOpenPropertyExpression(expr))
            {
                return(OpenTypeMethods.NotExpression(expr));
            }

            if (expr.Type == typeof(bool) || expr.Type == typeof(Nullable <bool>))
            {
                // Expression.Not will take numerics and apply '~' to them, thus the extra check here.
                return(Expression.Not(expr));
            }
            else
            {
                string message = Strings.RequestQueryParser_NotDoesNotSupportType(expr.Type);
                throw DataServiceException.CreateSyntaxError(message);
            }
        }
Esempio n. 7
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);
            }
        }