/// <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) { // Store the type of the 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.query.ProjectionArgumentType = projection.Parameters[0].Type; // Compile the projection into a function that we can apply // to the deserialized value to transform it accordingly. this.query.Projection = projection.Compile(); // Filter the selection down to just the values used by // the projection VisitorHelper.VisitMembers( projection.Body, (expr, recur) => { // Ensure we only process members of the parameter if (expr != null && expr.Expression.NodeType == ExpressionType.Parameter) { SerializableMember member = SerializableType.GetMember(expr.Member); if (member != null) { query.Selection.Add(member.Name); } } return(recur(expr)); }); // Make sure we also include all the members that would be // required for deserialization foreach (SerializableMember member in SerializableType.Get(this.query.ProjectionArgumentType) .Members .Select(p => p.Value) .Where(m => m.IsRequired)) { if (!this.query.Selection.Contains(member.Name)) { this.query.Selection.Add(member.Name); } } return; } } ThrowForUnsupportedException(expression); }
/// <summary> /// Get the table member referenced by an expression or return null. /// </summary> /// <param name="expression">The expression to check.</param> /// <returns>The table member or null.</returns> protected static SerializableMember GetTableMember(Expression expression) { // Only parameter references are valid in a query (any other // references should have been partially evaluated away) MemberExpression member = expression as MemberExpression; if (member != null && member.Expression != null && member.Expression.NodeType == ExpressionType.Parameter && member.Member != null) { // Lookup the Mobile Services name of the member and use that return(SerializableType.GetMember(member.Member)); } // Otherwise return null return(null); }
/// <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> private void AddOrdering(MethodCallExpression expression, bool ascending) { // 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) { if (memberAccess.Expression.NodeType == ExpressionType.Parameter) { SerializableMember member = SerializableType.GetMember(memberAccess.Member); if (member != null && member.Name != null) { // Add the ordering this.query.Ordering.Add(new KeyValuePair <string, bool>(member.Name, ascending)); return; } } } } } throw new NotSupportedException( string.Format( CultureInfo.InvariantCulture, Resources.MobileServiceTableQueryTranslator_GetOrdering_Unsupported, expression != null && expression.Method != null ? expression.Method.Name : null, deepest != null ? deepest.ToString() : null)); }