SortArgument GetSortArgumentFromSortCall(MethodCallExpression node) { if (node.Arguments[1].NodeType == ExpressionType.Quote) { UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1) { SafeExpressionImporter importer = new SafeExpressionImporter(lambda.Parameters[0]); Expression imported = importer.Import(lambda.Body); if (imported != null) { return(new SortArgument( Expression.Lambda(imported, lambda.Parameters[0]), IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderByDesc) || IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_ThenByDesc) )); } } } } return(null); }
SortArgument GetSortArgumentFromSortCall(MethodCallExpression node) { if (node.Arguments[1].NodeType == ExpressionType.Quote) { UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1) { SafeExpressionImporter importer = new SafeExpressionImporter(lambda.Parameters[0]); Expression imported = importer.Import(lambda.Body); if (imported != null) return new SortArgument( Expression.Lambda(imported, lambda.Parameters[0]), IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderByDesc) || IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_ThenByDesc) ); } } } return null; }
protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method == KnownMembers.QueryableOfCallTreeNode_Select && node.Arguments[1].NodeType == ExpressionType.Quote) { // CallTreeNode[].Select: // selects are not supported by query evaluator, but we will detect and remove // degenerate selects (.Select(x => x)). UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1 && lambda.Body == lambda.Parameters[0]) { return(Visit(node.Arguments[0])); } } } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Where && node.Arguments[1].NodeType == ExpressionType.Quote) { // CallTreeNode[].Where: // if the target is a QueryNode and the condition can be safely imported, convert the Where call to Filter UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { SafeExpressionImporter importer = new SafeExpressionImporter(lambda.Parameters[0]); List <LambdaExpression> conditions = new List <LambdaExpression>(); if (importer.AddConditionsToFilterList(lambda.Body, conditions)) { return(new Filter(target, conditions.ToArray())); } } } } } else if (node.Method == KnownMembers.Queryable_WithQueryLog && node.Arguments[1].NodeType == ExpressionType.Constant) { options.AddLogger((TextWriter)(((ConstantExpression)node.Arguments[1]).Value)); return(Visit(node.Arguments[0])); } else if (node.Method == KnownMembers.Queryable_MergeByName) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { return(new MergeByName(target)); } } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Take && node.Arguments[1].NodeType == ExpressionType.Constant) { ConstantExpression ce = (ConstantExpression)node.Arguments[1]; if (ce.Type == typeof(int)) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { return(new Limit(target, 0, (int)ce.Value)); } } } else if (IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderBy) || IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderByDesc)) { SortArgument sortArgument = GetSortArgumentFromSortCall(node); if (sortArgument != null) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { return(new Sort(target, new [] { sortArgument })); } } } else if (IsThenByCall(node)) { // 'ThenBy' is dangerous: we must not translate an OrderBy inside a ThenBy that we do not support List <MethodCallExpression> thenByCalls = new List <MethodCallExpression>(); Expression tmp = node; while (IsThenByCall(tmp)) { thenByCalls.Add((MethodCallExpression)tmp); tmp = ((MethodCallExpression)tmp).Arguments[0]; } MethodCallExpression mc = tmp as MethodCallExpression; if (mc != null && (IsGenericMethodInfoOfCallTreeNode(mc, KnownMembers.Queryable_OrderBy) || IsGenericMethodInfoOfCallTreeNode(mc, KnownMembers.Queryable_OrderByDesc))) { // TODO: add support for safe expressions in ThenBy // this is an unsupported OrderBy/ThenBy sequence // skip visiting the sequence and go directly into the base object tmp = Visit(mc.Arguments[0]); // now reconstruct the OrderBy/ThenBy sequence tmp = Expression.Call(mc.Method, new[] { tmp }.Concat(mc.Arguments.Skip(1))); // reconstruct OrderBy for (int i = thenByCalls.Count - 1; i >= 0; i--) { // reconstruct ThenBy tmp = Expression.Call(thenByCalls[i].Method, new[] { tmp }.Concat(thenByCalls[i].Arguments.Skip(1))); } return(tmp); } // else: we couldn't detect the OrderBy belonging to this ThenBy; so it's probably not one // of the OrderBy overloads that we support. Go down recursively } return(base.VisitMethodCall(node)); }
protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method == KnownMembers.QueryableOfCallTreeNode_Select && node.Arguments[1].NodeType == ExpressionType.Quote) { // CallTreeNode[].Select: // selects are not supported by query evaluator, but we will detect and remove // degenerate selects (.Select(x => x)). UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1 && lambda.Body == lambda.Parameters[0]) return Visit(node.Arguments[0]); } } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Where && node.Arguments[1].NodeType == ExpressionType.Quote) { // CallTreeNode[].Where: // if the target is a QueryNode and the condition can be safely imported, convert the Where call to Filter UnaryExpression quote = (UnaryExpression)node.Arguments[1]; if (quote.Operand.NodeType == ExpressionType.Lambda) { LambdaExpression lambda = (LambdaExpression)quote.Operand; if (lambda.Parameters.Count == 1) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { SafeExpressionImporter importer = new SafeExpressionImporter(lambda.Parameters[0]); List<LambdaExpression> conditions = new List<LambdaExpression>(); if (importer.AddConditionsToFilterList(lambda.Body, conditions)) { return new Filter(target, conditions.ToArray()); } } } } } else if (node.Method == KnownMembers.Queryable_WithQueryLog && node.Arguments[1].NodeType == ExpressionType.Constant) { options.AddLogger((TextWriter)(((ConstantExpression)node.Arguments[1]).Value)); return Visit(node.Arguments[0]); } else if (node.Method == KnownMembers.Queryable_MergeByName) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) return new MergeByName(target); } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Take && node.Arguments[1].NodeType == ExpressionType.Constant) { ConstantExpression ce = (ConstantExpression)node.Arguments[1]; if (ce.Type == typeof(int)) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { return new Limit(target, 0, (int)ce.Value); } } } else if (IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderBy) || IsGenericMethodInfoOfCallTreeNode(node, KnownMembers.Queryable_OrderByDesc)) { SortArgument sortArgument = GetSortArgumentFromSortCall(node); if (sortArgument != null) { QueryNode target = Visit(node.Arguments[0]) as QueryNode; if (target != null) { return new Sort(target, new [] { sortArgument }); } } } else if (IsThenByCall(node)) { // 'ThenBy' is dangerous: we must not translate an OrderBy inside a ThenBy that we do not support List<MethodCallExpression> thenByCalls = new List<MethodCallExpression>(); Expression tmp = node; while (IsThenByCall(tmp)) { thenByCalls.Add((MethodCallExpression)tmp); tmp = ((MethodCallExpression)tmp).Arguments[0]; } MethodCallExpression mc = tmp as MethodCallExpression; if (mc != null && (IsGenericMethodInfoOfCallTreeNode(mc, KnownMembers.Queryable_OrderBy) || IsGenericMethodInfoOfCallTreeNode(mc, KnownMembers.Queryable_OrderByDesc))) { // TODO: add support for safe expressions in ThenBy // this is an unsupported OrderBy/ThenBy sequence // skip visiting the sequence and go directly into the base object tmp = Visit(mc.Arguments[0]); // now reconstruct the OrderBy/ThenBy sequence tmp = Expression.Call(mc.Method, new[] { tmp }.Concat(mc.Arguments.Skip(1))); // reconstruct OrderBy for (int i = thenByCalls.Count - 1; i >= 0; i--) { // reconstruct ThenBy tmp = Expression.Call(thenByCalls[i].Method, new[] { tmp }.Concat(thenByCalls[i].Arguments.Skip(1))); } return tmp; } // else: we couldn't detect the OrderBy belonging to this ThenBy; so it's probably not one // of the OrderBy overloads that we support. Go down recursively } return base.VisitMethodCall(node); }