public static ODataQueryExpression Parse(Type elementType, NameValueCollection parameters) { Throw.IfNull(elementType, "elementType"); Throw.IfNull(parameters, "parameters"); ODataExpression filter = null; var filterString = parameters["$filter"]; if (!string.IsNullOrEmpty(filterString)) { var parser = new ODataExpressionLanguageParser(elementType, filterString); filter = parser.Parse(); } IReadOnlyList <ODataSortKeyExpression> orderBy = Empty <ODataSortKeyExpression> .Array; var orderByString = parameters["$orderby"]; if (!string.IsNullOrEmpty(orderByString)) { var parser = new ODataExpressionLanguageParser(elementType, orderByString); orderBy = parser.ParseSortKeyList(); } int?top = null; var topString = parameters["$top"]; if (!string.IsNullOrEmpty(topString)) { int topValue; if (!int.TryParse(topString, out topValue)) { throw new ODataParseException("Expected an integer value for $top: " + topString); } top = topValue; } var skip = 0; var skipString = parameters["$skip"]; if (!string.IsNullOrEmpty(skipString)) { if (!int.TryParse(skipString, out skip)) { throw new ODataParseException("Expected an integer value for $skip: " + skipString); } } IReadOnlyList <ODataSelectColumnExpression> select = Empty <ODataSelectColumnExpression> .Array; var selectString = parameters["$select"]; if (!string.IsNullOrEmpty(selectString)) { var parser = new ODataExpressionLanguageParser(elementType, selectString); select = parser.ParseSelectColumnList(); } var formatParameter = parameters["$format"]; string format; if (!string.IsNullOrEmpty(formatParameter)) { Throw <ODataParseException> .If(string.IsNullOrWhiteSpace(formatParameter), "$format may not be whitespace"); format = formatParameter; } else { format = null; } var inlineCount = ODataInlineCountOption.None; var inlineCountString = parameters["$inlinecount"]; if (!string.IsNullOrEmpty(inlineCountString)) { if (!Enum.TryParse(inlineCountString, ignoreCase: true, result: out inlineCount)) { throw new ODataParseException("Unexpected value " + inlineCountString + " for $inlinecount"); } } return(ODataExpression.Query( filter: filter, orderBy: orderBy, top: top, skip: skip, select: select, format: format, inlineCount: inlineCount )); }
private ODataExpression TranslateInternal(Expression linq) { if (linq == null) { return(null); } // captured parameters object value; if (TryGetValueFast(linq, out value)) { // special case the handling of queryable constants, since these can be the "root" // of the query expression tree. We check for isInsideQuery since we don't allow multiple // roots. An example of a multiply-rooted tree would be: q.Where(x => q.Any(xx => xx.A < x.A)) if (!this._isInsideQuery && typeof(IQueryable).GetTypeInfo().IsAssignableFrom(linq.Type.GetTypeInfo())) { this._rootQuery = (IQueryable)value; return(ODataExpression.Query()); } return(ODataExpression.Constant(value, linq.Type)); } switch (linq.NodeType) { case ExpressionType.OrElse: return(this.TranslateBinary(linq, ODataBinaryOp.Or)); case ExpressionType.AndAlso: return(this.TranslateBinary(linq, ODataBinaryOp.And)); case ExpressionType.Add: if (linq.Type == typeof(string)) { // special case adding strings, since that's the same as Concat var binary = (BinaryExpression)linq; return(this.TranslateInternal(Expression.Call(Helpers.GetMethod(() => string.Concat(default(string), default(string))), binary.Left, binary.Right))); } return(this.TranslateBinary(linq, ODataBinaryOp.Add)); case ExpressionType.Subtract: return(this.TranslateBinary(linq, ODataBinaryOp.Subtract)); case ExpressionType.Multiply: return(this.TranslateBinary(linq, ODataBinaryOp.Multiply)); case ExpressionType.Divide: return(this.TranslateBinary(linq, ODataBinaryOp.Divide)); case ExpressionType.Modulo: return(this.TranslateBinary(linq, ODataBinaryOp.Modulo)); case ExpressionType.Equal: return(this.TranslateBinary(linq, ODataBinaryOp.Equal)); case ExpressionType.NotEqual: return(this.TranslateBinary(linq, ODataBinaryOp.NotEqual)); case ExpressionType.LessThan: return(this.TranslateBinary(linq, ODataBinaryOp.LessThan)); case ExpressionType.LessThanOrEqual: return(this.TranslateBinary(linq, ODataBinaryOp.LessThanOrEqual)); case ExpressionType.GreaterThan: return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThan)); case ExpressionType.GreaterThanOrEqual: return(this.TranslateBinary(linq, ODataBinaryOp.GreaterThanOrEqual)); case ExpressionType.Not: return(ODataExpression.UnaryOp(this.TranslateInternal(((UnaryExpression)linq).Operand), ODataUnaryOp.Not)); case ExpressionType.Negate: // we translate -a.B as (-1 * a.B) for numeric types var negated = ((UnaryExpression)linq).Operand; var underlyingNegatedType = Nullable.GetUnderlyingType(negated.Type) ?? negated.Type; if (!underlyingNegatedType.IsNumeric()) { throw new ODataCompileException("Expression '" + linq + "' could not be translated to OData: only negation of numeric types is supported"); } var multiplyNegate = Expression.Multiply( Expression.Constant(Convert.ChangeType(-1, underlyingNegatedType), negated.Type), negated ); return(this.TranslateInternal(multiplyNegate)); case ExpressionType.MemberAccess: return(this._memberAndParameterTranslator.TranslateMemberAccess((MemberExpression)linq)); case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.TypeAs: return(ODataExpression.Convert(this.TranslateInternal(((UnaryExpression)linq).Operand), linq.Type)); case ExpressionType.Parameter: return(this._memberAndParameterTranslator.TranslateParameter((ParameterExpression)linq)); case ExpressionType.TypeIs: var typeBinary = (TypeBinaryExpression)linq; var isArg = this.TranslateInternal(typeBinary.Expression); var isTypeConstant = ODataExpression.Constant(typeBinary.TypeOperand); return(isArg != null ? ODataExpression.Call(ODataFunction.IsOf, new[] { isArg, isTypeConstant }) : ODataExpression.Call(ODataFunction.IsOf, new[] { isTypeConstant })); case ExpressionType.Call: return(this.TranslateCall((MethodCallExpression)linq)); case ExpressionType.Quote: var quoted = (LambdaExpression)((UnaryExpression)linq).Operand; if (!this._isInsideQuery) { throw new ODataCompileException("Unexpected placement for lambda expression " + linq); } return(this.TranslateInternal(quoted.Body)); default: throw new ODataCompileException("Expression '" + linq + "' of type " + linq.NodeType + " could not be translated to OData"); } }