protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method.IsQueryableOrEnumerableMethod() && !node.ContainsNonLambdaExpressions() && !node.ContainsNonLambdaDelegates()) { var arguments = Visit(node.Arguments); switch (node.Method.Name) { case nameof(Queryable.Select): { if (arguments[0] is ProjectionExpression projection) { return(projection.Merge(arguments[1].UnwrapLambda())); } return(node); } case nameof(Queryable.SelectMany): { if (arguments[0] is ProjectionExpression outerProjection) { var innerProjection = arguments[1] as ProjectionExpression; if (innerProjection == null) { var collectionSelectorLambda = arguments[1].UnwrapLambda(); if (collectionSelectorLambda != null) { var expanded = collectionSelectorLambda .ExpandParameters(outerProjection.Flatten().Body); innerProjection = Visit(expanded) as ProjectionExpression; } } if (innerProjection != null) { if (arguments.Count == 2) { return(innerProjection); } else { return(new CompositeProjectionExpression( outerProjection, innerProjection, arguments[2].UnwrapLambda())); } } } return(node); } case nameof(Queryable.Join): { if (arguments[0] is ProjectionExpression outerProjection && arguments[1] is ProjectionExpression innerProjection) { return(new CompositeProjectionExpression( outerProjection, innerProjection, arguments[4].UnwrapLambda())); } return(node); } case nameof(Queryable.GroupJoin): { if (arguments[0] is ProjectionExpression outerProjection && arguments[1] is ProjectionExpression innerProjection) { return(new CompositeProjectionExpression( outerProjection, new ServerProjectionExpression( new SurrogateEnumerableRelationalQueryExpression( new SelectExpression(innerProjection))), arguments[4].UnwrapLambda())); } return(node); } case nameof(Queryable.GroupBy): { if (arguments[0] is ProjectionExpression projection && node.Method.HasResultSelector()) { var keyExpression = projection.Merge(arguments[1].UnwrapLambda()).Flatten().Body; var elementProjectionBody = projection.Flatten().Body; if (node.Method.HasElementSelector()) { elementProjectionBody = projection.Merge(arguments[2].UnwrapLambda()).Flatten().Body; } var elementExpression = new SurrogateEnumerableRelationalQueryExpression( new SelectExpression( new ServerProjectionExpression( elementProjectionBody))); //if (node.Method.HasResultSelector()) { var resultSelector = node.Method.HasElementSelector() ? arguments[3] : arguments[2]; return(new ServerProjectionExpression( resultSelector .UnwrapLambda() .ExpandParameters(keyExpression, elementExpression))); } /*return new ServerProjectionExpression( * ExpandedGrouping.Create( * node.Type.GetSequenceType(), * keyExpression, * elementExpression.AsList()));*/ } return(node); } case nameof(Queryable.Zip): { if (arguments[0] is ProjectionExpression outerProjection && arguments[1] is ProjectionExpression innerProjection) { return(new CompositeProjectionExpression( outerProjection, innerProjection, arguments[2].UnwrapLambda())); } return(node); } case nameof(Queryable.All): case nameof(Queryable.Any): case nameof(Queryable.Contains): case nameof(Queryable.Count): case nameof(Queryable.LongCount): case nameof(Queryable.Max): case nameof(Queryable.Min): case nameof(Queryable.Average): case nameof(Queryable.Sum): { return(new ServerProjectionExpression(Expression.Default(node.Type))); } case nameof(Enumerable.ToArray): case nameof(Enumerable.ToDictionary): //case nameof(Enumerable.ToHashSet): case nameof(Enumerable.ToList): case nameof(Enumerable.ToLookup): default: { if (arguments[0] is ProjectionExpression projection) { return(projection); } return(node); } } } if (node.Method.DeclaringType == typeof(Enumerable) && node.Method.Name == nameof(Enumerable.AsEnumerable)) { var arguments = Visit(node.Arguments); if (arguments[0] is ProjectionExpression projection) { return(projection); } } return(node); }