public Filter(QueryNode target, params LambdaExpression[] conditions) : base(target) { Debug.Assert(target != null); foreach (LambdaExpression l in conditions) { Debug.Assert(l.Parameters.Count == 1); } this.Conditions = Array.AsReadOnly(conditions); }
public Sort(QueryNode target, IList<SortArgument> args) : base(target) { this.arguments = new ReadOnlyCollection<SortArgument>(args); }
protected QueryNode(QueryNode target) { this.Target = target; }
public MergeByName(QueryNode target) : base(target) { Debug.Assert(target != 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)); }
public Limit(QueryNode target, int start, int length) : base(target) { this.Start = start; this.Length = length; }
public Sort(QueryNode target, IList <SortArgument> args) : base(target) { this.arguments = new ReadOnlyCollection <SortArgument>(args); }
public IQueryable <CallTreeNode> CreateQuery(QueryNode query) { return(new Query <CallTreeNode>(this, query)); }
QueryNode Visit(QueryNode queryNode) { return (QueryNode)base.Visit(queryNode); }
public IQueryable<CallTreeNode> CreateQuery(QueryNode query) { return new Query<CallTreeNode>(this, query); }
QueryNode Visit(QueryNode queryNode) { return((QueryNode)base.Visit(queryNode)); }
QueryNode VisitMergeByName(MergeByName merge) { // First optimize the Target expression QueryNode target = Visit(merge.Target); if (target is MergeByName) { // x.MergeByName().MergeByName() -> x.MergeByName() return(target); } if (target == AllCalls.Instance) { // AllCalls.MergeByName() -> AllFunctions return(new AllFunctions()); } if (target is Filter && target.Target == AllCalls.Instance) { // AllCalls.Filter(criteria).MergeByName() -> AllFunctions.Filter(criteria) // If criteria accesses no CallTreeNode properties except for NameMapping. // Criteria of the form 'start <= c.DataSetID && c.DataSetID <= end' will be converted into AllFunctions(start,end) List <LambdaExpression> newConditions = new List <LambdaExpression>(); bool allIsSafe = true; int startDataSetID = -1; int endDataSetID = -1; foreach (LambdaExpression condition in ((Filter)target).Conditions) { if (IsConditionSafeVisitor.Test(condition, SafeMembersForMoveIntoMergeByName)) { newConditions.Add(condition); } else if (condition.Body.NodeType == ExpressionType.AndAlso && startDataSetID < 0) { // try match 'constant <= c.DataSetID && c.DataSetID <= constant', but only if we // haven't found it already (startDataSetID is still -1) BinaryExpression bin = (BinaryExpression)condition.Body; if (bin.Left.NodeType == ExpressionType.LessThanOrEqual && bin.Right.NodeType == ExpressionType.LessThanOrEqual) { BinaryExpression left = (BinaryExpression)bin.Left; BinaryExpression right = (BinaryExpression)bin.Right; if (left.Left.NodeType == ExpressionType.Constant && left.Right.NodeType == ExpressionType.MemberAccess && right.Left.NodeType == ExpressionType.MemberAccess && right.Right.NodeType == ExpressionType.Constant && ((MemberExpression)left.Right).Member == SingleCall.DataSetIdField && ((MemberExpression)right.Left).Member == SingleCall.DataSetIdField) { startDataSetID = (int)GetConstantValue(left.Left); endDataSetID = (int)GetConstantValue(right.Right); } else { allIsSafe = false; } } else { allIsSafe = false; } } else { allIsSafe = false; } } if (allIsSafe) { if (newConditions.Count > 0) { return(new Filter(new AllFunctions(startDataSetID, endDataSetID), newConditions.ToArray())); } else { return(new AllFunctions(startDataSetID, endDataSetID)); } } } return(new MergeByName(target)); }