/// <summary> /// Unify all ordering expressions into a single order expression /// </summary> private void FlattenOrderExpressions() { List <ServiceQueryPart> newList = new List <ServiceQueryPart>(); ServiceQueryPart orderPart = null; foreach (ServiceQueryPart part in this._queryParts) { if (part.QueryOperator.StartsWith("orderby", StringComparison.Ordinal)) { orderPart = part; } if (part.QueryOperator.StartsWith("thenby", StringComparison.Ordinal)) { // accumulate secondary ordering expressions orderPart.Expression += ", " + part.Expression; } else { newList.Add(part); } } this._queryParts = newList; }
protected override Expression VisitMethodCall(MethodCallExpression m) { ServiceQueryPart holdPart = this._currPart; StringBuilder holdPartBuilder = this._currPartBuilder; string methodName = CultureInfo.InvariantCulture.TextInfo.ToLower(m.Method.Name); if (IsSequenceOperatorCall(m)) { if (methodName == "where" || methodName == "orderby" || methodName == "orderbydescending" || methodName == "skip" || methodName == "take" || methodName == "thenby" || methodName == "thenbydescending") { this._currPart = new ServiceQueryPart(); this._currPartBuilder = new StringBuilder(); this._currPart.QueryOperator = methodName; this._queryParts.Add(this._currPart); } else if (methodName == "select") { // if the selection specified is anything other than an // empty selector, throw an exception. I.e. only Select(p => p) // is supported. bool isEmptySelection = false; Expression selector = m.Arguments[1]; if (selector.NodeType == ExpressionType.Quote) { LambdaExpression lex = (LambdaExpression)((UnaryExpression)m.Arguments[1]).Operand; isEmptySelection = (lex.Body.NodeType == ExpressionType.Parameter) && (lex.Parameters.Single() == lex.Body); } if (!isEmptySelection) { throw new NotSupportedException(Resources.QuerySerialization_ProjectionsNotSupported); } } else { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.QuerySerialization_UnsupportedQueryOperator, m.Method.Name)); } Expression obj = this.Visit(m.Object); IEnumerable <Expression> args = this.Visit(m.Arguments); if (obj != m.Object || args != m.Arguments) { return(Expression.Call(obj, m.Method, args)); } // post processing if (methodName == "orderbydescending" || methodName == "thenbydescending") { if (methodName == "orderbydescending") { this._currPart.QueryOperator = "orderby"; } else { this._currPart.QueryOperator = "thenby"; } this._currPartBuilder.Append(" desc"); } } else if (m.Method.DeclaringType == typeof(Enum) && m.Method.Name == "HasFlag") { // Serialize (p => p.enumProp.HasFlag( EnumType.A)) into "(it.enumProp has EnumType.A)" this._currPartBuilder.Append("("); this.Visit(m.Object); // We could convert it to an int here if possible, otherwise we will do it anyway on the server //this.Visit(Expression.Convert(m.Object, typeof(int))); this._currPartBuilder.Append(" has "); // We could convert it to an int here if possible, otherwise we will do it anyway on the server //this.Visit(Expression.Convert(m.Arguments[0], typeof(int))); this.Visit(m.Arguments[0]); this._currPartBuilder.Append(")"); } else { // Ensure that the method is accessible // Only methods on supported predefined types are allowed VerifyMethodAccessibility(m); if (m.Method.IsStatic) { this._currPartBuilder.Append(m.Method.DeclaringType.Name); } else { this.Visit(m.Object); } if (m.Object == null || m.Object.NodeType != ExpressionType.Parameter) { // for all member accesses other than those directly off of // our query parameter, we need to append a dot this._currPartBuilder.Append("."); } this._currPartBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}(", m.Method.Name); this.VisitMethodParameters(m.Arguments); this._currPartBuilder.Append(")"); } if (this._currPart != null) { this._currPart.Expression = this._currPartBuilder.ToString(); } this._currPart = holdPart; this._currPartBuilder = holdPartBuilder; return(m); }