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