public static IQueryable <T> IncludeSubPath <T>(IQueryable <T> query, Expression expression) { if (!QueryIncludeFilterManager.AllowIncludeSubPath) { return(query); } var pathVisitor = new QueryIncludeFilterExpressionToReduceVisitor { RootExpression = expression }; pathVisitor.Visit(expression); // GET all expression to reduce if (pathVisitor.Expressions.Count > 0) { foreach (var node in pathVisitor.Expressions) { var pathReduceVisitor = new QueryIncludeFilterExpressionReduceVisitor { RootExpression = expression, NodeToReduce = node }; // REDUCE the node var nodeReduced = pathReduceVisitor.Visit(expression); // ENSURE the node is reduced if (nodeReduced == node) { throw new Exception(ExceptionMessage.QueryIncludeFilter_NodeReduce); } var typeSource = typeof(T); var elementType = nodeReduced.Type.Name == "Func`2" ? nodeReduced.Type.GetGenericArguments()[1] : nodeReduced.Type; var method = typeof(QueryIncludeFilterManager).GetMethod("IncludeFilterSingleLazy", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(typeSource, elementType); // CALL IncludeFilter with the new expression query = (IQueryable <T>)method.Invoke(null, new object[] { query, nodeReduced }); } } return(query); }
/// <summary> /// Visits the children of the <see cref="T:System.Linq.Expressions.Expression`1" />. /// </summary> /// <typeparam name="T">The type of the delegate.</typeparam> /// <param name="node">The expression to visit.</param> /// <returns> /// The modified expression, if it or any subexpression was modified; otherwise, returns the /// original expression. /// </returns> protected override Expression VisitLambda <T>(Expression <T> node) { if (node == RootExpression || LambdaToChecks.Contains(node)) { var currentNode = node.Body; var memberExpression = node.Body as MemberExpression; if (memberExpression != null) { Expression outExpression; if (AddMemberExpression(node, memberExpression, out outExpression)) { return(outExpression); } } else { MethodCallExpression callExpression; while ((callExpression = currentNode as MethodCallExpression) != null) { memberExpression = callExpression.Arguments[0] as MemberExpression; if (memberExpression != null) { Expression outExpression; if (AddMemberExpression(node, memberExpression, out outExpression)) { return(outExpression); } } var isSelectMethod = callExpression.Method.ReflectedType != null && callExpression.Method.ReflectedType.FullName == "System.Linq.Enumerable" && (callExpression.Method.Name == "Select" || callExpression.Method.Name == "SelectMany"); if (isSelectMethod) { // ALL Select && SelectMany method are modified but only the last is reduced if (callExpression == NodeToReduce) { // ADD // x => x.Many.Select(y => Many.Select(z => z.Many) to x.Many.Select(y => y.Many) // x => x.Many.Select(y => y.Many) to x => x.Many var reduceExpression = callExpression.Arguments[0]; var isEnumerable = reduceExpression.Type.GetGenericArguments().Length == 1; if (isEnumerable) { var typeSource = node.Parameters[0].Type; var elementType = reduceExpression.Type.GetGenericArguments()[0]; var genericFunc = typeof(Func <,>).MakeGenericType(typeSource, typeof(IEnumerable <>).MakeGenericType(elementType)); var lambdaMethod = typeof(Expression).GetMethods() .Single(x => x.Name == "Lambda" && x.IsGenericMethod && x.GetParameters().Length == 2 && !x.GetParameters()[1].ParameterType.IsArray).MakeGenericMethod(genericFunc); var newExpression = (Expression)lambdaMethod.Invoke(null, new object[] { reduceExpression, node.Parameters }); return(newExpression); } else { var sourceType = node.Parameters[0].Type; var elementType = reduceExpression.Type; var genericFunc = typeof(Func <,>).MakeGenericType(sourceType, elementType); var lambdaMethod = typeof(Expression).GetMethods() .Single(x => x.Name == "Lambda" && x.IsGenericMethod && x.GetParameters().Length == 2 && !x.GetParameters()[1].ParameterType.IsArray).MakeGenericMethod(genericFunc); var newExpression = (LambdaExpression)lambdaMethod.Invoke(null, new object[] { reduceExpression, node.Parameters }); return(newExpression); } } // TRY reduce lambda... { var visitor = new QueryIncludeFilterExpressionReduceVisitor(); visitor.RootExpression = callExpression.Arguments[1]; visitor.NodeToReduce = NodeToReduce; var reducedExpression = visitor.Visit(callExpression.Arguments[1]); if (reducedExpression != callExpression.Arguments[1]) { MethodCallExpression reducedMethod; // FIX method { var baseMethod = callExpression.Method.GetGenericMethodDefinition(); var sourceType = callExpression.Arguments[0].Type.GetGenericArguments()[0]; var elementType = callExpression.Method.Name == "SelectMany" ? reducedExpression.Type.GetGenericArguments()[1].GetGenericArguments()[0] : reducedExpression.Type.GetGenericArguments()[1]; var genericMethod = baseMethod.MakeGenericMethod(sourceType, elementType); var arguments = callExpression.Arguments.ToList(); arguments[1] = reducedExpression; reducedMethod = Expression.Call(null, genericMethod, arguments); } // FIX lambda expression { var typeSource = node.Parameters[0].Type; var elementType = reducedMethod.Type.GetGenericArguments()[0]; var genericFunc = typeof(Func <,>).MakeGenericType(typeSource, typeof(IEnumerable <>).MakeGenericType(elementType)); var lambdaMethod = typeof(Expression).GetMethods() .Single(x => x.Name == "Lambda" && x.IsGenericMethod && x.GetParameters().Length == 2 && !x.GetParameters()[1].ParameterType.IsArray).MakeGenericMethod(genericFunc); var newExpression = (Expression)lambdaMethod.Invoke(null, new object[] { reducedMethod, node.Parameters }); return(newExpression); } } } } currentNode = callExpression.Arguments[0]; } } } var baseExpression = base.VisitLambda(node); return(baseExpression); }