/// <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);
                    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);
        }
        /// <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)
            {
                LambdaExpression projection = StripQuote(expression.Arguments[1]) as LambdaExpression;
                if (projection != null && 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;

                        foreach (MemberExpression memberExpression in GetMemberExpressions(projection.Body))
                        {
                            // Ensure we only process members of the parameter
                            string memberName = FilterBuildingExpressionVisitor.GetTableMemberName(memberExpression);
                            if (memberName != null)
                            {
                                queryDescription.Selection.Add(memberName);
                            }
                        }

                        ////Make sure we also include all the members that would be
                        ////required for deserialization
                        //                       if (objectContract != null)
                        //{
                        //    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)
        {
            Debug.Assert(expression != null);


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

            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);
                        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));
        }