/// <summary>
        /// Add a filter expression to the query.
        /// </summary>
        /// <param name="expression">
        /// Where method call expression.
        /// </param>
        private void AddFilter(MethodCallExpression expression)
        {
            if (expression != null && expression.Arguments.Count >= 2)
            {
                LambdaExpression lambda = StripQuote(expression.Arguments[1]) as LambdaExpression;
                if (lambda != null)
                {
                    QueryNode filter = FilterBuildingExpressionVisitor.Compile(lambda.Body, this.ContractResolver);
                    if (this.queryDescription.Filter != null)
                    {
                        // If there's already a filter value, that means the
                        // query has multiple where clauses which we'll just
                        // join together with "and"s.
                        this.queryDescription.Filter = new BinaryOperatorNode(BinaryOperatorKind.And, this.queryDescription.Filter, filter);
                    }
                    else
                    {
                        this.queryDescription.Filter = filter;
                    }
                    return;
                }
            }

            ThrowForUnsupportedException(expression);
        }
Example #2
0
        /// <summary>
        /// Add a projection to the query.
        /// </summary>
        /// <param name="expression">
        /// Select method call expression.
        /// </param>
        private void AddProjection(MethodCallExpression expression)
        {
            // We only support Select(x => ...) projections.  Anything else
            // will throw a NotSupportException.
            if (expression != null && expression.Arguments.Count == 2)
            {
                if (StripQuote(expression.Arguments[1]) is LambdaExpression projection && projection.Parameters.Count == 1)
                {
                    // Compile the projection into a function that we can apply
                    // to the deserialized value to transform it accordingly.
                    this.queryDescription.Projections.Add(projection.Compile());

                    // We only need to capture the projection argument type and members for the
                    // very first projection.
                    if (this.queryDescription.ProjectionArgumentType == null)
                    {
                        // Store the type of the very first input to the projection as we'll
                        // need that for deserialization of values (since the
                        // projection will change the expected type of the data
                        // source)
                        this.queryDescription.ProjectionArgumentType = projection.Parameters[0].Type;

                        // Filter the selection down to just the values used by
                        // the projection
                        IExpressionUtility expressionUtility = Platform.Instance.ExpressionUtility;
                        foreach (MemberExpression memberExpression in expressionUtility.GetMemberExpressions(projection.Body))
                        {
                            // Ensure we only process members of the parameter
                            string memberName = FilterBuildingExpressionVisitor.GetTableMemberName(memberExpression, this.ContractResolver);
                            if (memberName != null)
                            {
                                queryDescription.Selection.Add(memberName);
                            }
                        }

                        //Make sure we also include all the members that would be
                        //required for deserialization
                        JsonContract contract = this.ContractResolver.ResolveContract(this.queryDescription.ProjectionArgumentType);
                        if (contract is JsonObjectContract objectContract)
                        {
                            foreach (string propertyName in objectContract.Properties
                                     .Where(p => p.Required == Required.Always ||
                                            p.Required == Required.AllowNull)
                                     .Select(p => p.PropertyName))
                            {
                                if (!this.queryDescription.Selection.Contains(propertyName))
                                {
                                    this.queryDescription.Selection.Add(propertyName);
                                }
                            }
                        }
                    }

                    return;
                }
            }

            ThrowForUnsupportedException(expression);
        }
        /// <summary>
        /// Translate an expression tree into a compiled OData query.
        /// </summary>
        /// <param name="expression">
        /// The expression tree.
        /// </param>
        /// <param name="contractResolver">
        /// The contract resolver used to determine property names from
        /// members used within expressions.
        /// </param>
        /// <returns>
        /// An OData query.
        /// </returns>
        public static QueryNode Compile(Expression expression, MobileServiceContractResolver contractResolver)
        {
            Arguments.IsNotNull(expression, nameof(expression));
            Arguments.IsNotNull(contractResolver, nameof(contractResolver));

            // Walk the expression tree and build the filter.
            FilterBuildingExpressionVisitor visitor = new FilterBuildingExpressionVisitor(contractResolver);

            visitor.Visit(expression);

            QueryNode node = visitor.filterExpression.FirstOrDefault();

            return(node);
        }
        /// <summary>
        /// Translate an expression tree into a compiled OData query.
        /// </summary>
        /// <param name="expression">
        /// The expression tree.
        /// </param>
        /// <param name="contractResolver">
        /// The contract resolver used to determine property names from
        /// members used within expressions.
        /// </param>
        /// <returns>
        /// An OData query.
        /// </returns>
        public static QueryNode Compile(Expression expression, MobileServiceContractResolver contractResolver)
        {
            Debug.Assert(expression != null);
            Debug.Assert(contractResolver != null);

            // Walk the expression tree and build the filter.
            FilterBuildingExpressionVisitor visitor = new FilterBuildingExpressionVisitor(contractResolver);

            visitor.Visit(expression);

            QueryNode node = visitor.filterExpression.FirstOrDefault();

            return(node);
        }
        /// <summary>
        /// Add an ordering constraint for an OrderBy/ThenBy call.
        /// </summary>
        /// <param name="expression">
        /// The ordering method call.
        /// </param>
        /// <param name="ascending">
        /// Whether the order is ascending or descending.
        /// </param>
        /// <param name="prepend">
        /// Indicates if the expression should be prepended or not.
        /// </param>
        private void AddOrdering(MethodCallExpression expression, bool ascending, bool prepend = false)
        {
            // Keep updating with the deepest nested expression structure we
            // can get to so that we can provide a more detailed error message
            Expression deepest = expression;

            // We only allow OrderBy(x => x.Member) expressions.  Anything else
            // will result in a NotSupportedException.
            if (expression != null && expression.Arguments.Count >= 2)
            {
                LambdaExpression lambda = StripQuote(expression.Arguments[1]) as LambdaExpression;
                if (lambda != null)
                {
                    deepest = lambda.Body ?? lambda;

                    // Find the name of the member being ordered
                    MemberExpression memberAccess = lambda.Body as MemberExpression;
                    if (memberAccess != null)
                    {
                        string memberName = FilterBuildingExpressionVisitor.GetTableMemberName(memberAccess, this.ContractResolver);
                        if (memberName != null)
                        {
                            OrderByDirection direction = ascending ? OrderByDirection.Ascending : OrderByDirection.Descending;
                            var node = new OrderByNode(new MemberAccessNode(null, memberName), direction);
                            // Add the ordering
                            if (prepend)
                            {
                                this.queryDescription.Ordering.Insert(0, node);
                            }
                            else
                            {
                                this.queryDescription.Ordering.Add(node);
                            }

                            return;
                        }
                    }
                }
            }

            throw new NotSupportedException(
                      string.Format(
                          CultureInfo.InvariantCulture,
                          "'{0}' Mobile Services query expressions must consist of members only, not '{1}'.",
                          expression != null && expression.Method != null ? expression.Method.Name : null,
                          deepest != null ? deepest.ToString() : null));
        }
        /// <summary>
        /// Translate an expression tree into a compiled OData query.
        /// </summary>
        /// <param name="expression">
        /// The expression tree.
        /// </param>
        /// <param name="contractResolver">
        /// The contract resolver used to determine property names from
        /// members used within expressions.
        /// </param>
        /// <returns>
        /// An OData query.
        /// </returns>
        public static QueryNode Compile(Expression expression, MobileServiceContractResolver contractResolver)
        {
            Debug.Assert(expression != null);
            Debug.Assert(contractResolver != null);
                    
            // Walk the expression tree and build the filter.
            FilterBuildingExpressionVisitor visitor = new FilterBuildingExpressionVisitor(contractResolver);
            visitor.Visit(expression);

            QueryNode node = visitor.filterExpression.FirstOrDefault();
            return node;
        }