/// <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) { // Add the ordering if (prepend) { this.queryDescription.Ordering.Insert(0, new KeyValuePair <string, bool>(memberName, ascending)); } else { this.queryDescription.Ordering.Add(new KeyValuePair <string, bool>(memberName, ascending)); } return; } } } } throw new NotSupportedException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableQueryTranslator_MembersOnlyInExpression, expression != null && expression.Method != null ? expression.Method.Name : null, deepest != null ? deepest.ToString() : null)); }
/// <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; // 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); JsonObjectContract objectContract = contract as JsonObjectContract; 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); }