/// <summary> /// Walk the expression and compute all of the subtrees that are not /// dependent on any of the expressions parameters. /// </summary> /// <param name="expression">The expression to analyze.</param> /// <returns> /// A set of all the expression subtrees that are independent from the /// expression's parameters. Note that this set will include every /// element of an independent subtree, but it's not a problem because /// the EvaluateIndependentSubtrees walks top down. /// </returns> private static HashSet <Expression> FindIndependentSubtrees(Expression expression) { HashSet <Expression> subtrees = new HashSet <Expression>(); // The dependent flag is closed over and used to communicate // between different layers of the recursive call bool dependent = false; // Walk the tree finding every VisitorHelper.VisitAll( expression, (expr, recur) => { if (expr != null) { // Cache whether or not our parent is already dependent // because we're going to reset the flag while we // evalute our descendents to see if all of our // subtrees are independent. bool parentIsDependent = dependent; // Set dependent to false and visit my entire subtree // to see if anything sets it to true dependent = false; recur(expr); // If nothing in my subtree is dependent if (!dependent) { // Then the current node is independent if it's not // related to the parameter or if it's not the // constant query root (to handle degenerate cases // where we don't actually use any parameters in // the query - like table.skip(2).take(3)). ConstantExpression constant = expr as ConstantExpression; if (expr.NodeType == ExpressionType.Parameter || (constant != null && constant.Value is IQueryable)) { dependent = true; } else { // Then the whole subtree starting at this node // is independent of any expression parameters // and safe for partial evaluation subtrees.Add(expr); } } dependent |= parentIsDependent; } return(expr); }); return(subtrees); }
/// <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> /// Returns the MemberExpressions in the expression hierarchy of the <paramref name="expression"/>. /// </summary> /// <param name="expression">The expression to search for children MemberExpressions</param> /// <returns>A collection of MemberExpressions.</returns> public IEnumerable <MemberExpression> GetMemberExpressions(Expression expression) { List <MemberExpression> members = new List <MemberExpression>(); VisitorHelper.VisitMembers( expression, (expr, recur) => { // Ensure we only process members of the parameter if (expr is MemberExpression) { members.Add((MemberExpression)expr); } return(recur(expr)); }); return(members); }
/// <summary> /// Walk the expression and compute all of the subtrees that are not /// dependent on any of the expressions parameters. /// </summary> /// <param name="expression">The expression to analyze.</param> /// <returns> /// A set of all the expression subtrees that are independent from the /// expression's parameters. Note that this set will include every /// element of an independent subtree, but it's not a problem because /// the EvaluateIndependentSubtrees walks top down. /// </returns> private static List <Expression> FindIndependentSubtrees(Expression expression) { List <Expression> subtrees = new List <Expression>(); // The dependent and isMemberInit flags are closed over and used to communicate // between different layers of the recursive call bool dependent = false; bool isMemberInit = false; // Walk the tree finding every VisitorHelper.VisitAll( expression, (expr, recur) => { if (expr != null) { // Cache whether or not our parent is already dependent // or a MemberInitExpression because we're going to reset // the flags while we evalute our descendents to see if all // of our subtrees are independent. bool parentIsDependent = dependent; bool parentIsMemberInit = isMemberInit; // Set flags to false and visit my entire subtree // to see if anything sets it to true dependent = false; isMemberInit = expr is MemberInitExpression; recur(expr); // If nothing in my subtree is dependent if (!dependent) { // A NewExpression itself will appear to be independent, // but if the parent is a MemberInitExpression, the NewExpression // can't be evaluated by itself. The MemberInitExpression will // determine if the full expression is dependent or not, so // the NewExpression should simply not be checked for dependency. if (expr is NewExpression newExpression && parentIsMemberInit) { return(expr); } // Then the current node is independent if it's not // related to the parameter or if it's not the // constant query root (to handle degenerate cases // where we don't actually use any parameters in // the query - like table.skip(2).take(3)). ConstantExpression constant = expr as ConstantExpression; if (expr.NodeType == ExpressionType.Parameter || (constant != null && constant.Value is IQueryable)) { dependent = true; } else { // Then the whole subtree starting at this node // is independent of any expression parameters // and safe for partial evaluation subtrees.Add(expr); } }