protected override Expression VisitMethodCall(MethodCallExpression node) { var @object = Visit(node.Object); var arguments = Visit(node.Arguments); if ((node.Method.DeclaringType == typeof(Queryable) || node.Method.DeclaringType == typeof(Enumerable)) && !node.ContainsNonLambdaDelegates() && arguments[0] is GroupByResultExpression relationalGrouping) { switch (node.Method.Name) { case nameof(Queryable.Average): case nameof(Queryable.Max) when node.Method.ReturnType.IsScalarType(): case nameof(Queryable.Min) when node.Method.ReturnType.IsScalarType(): case nameof(Queryable.Sum): { var selector = relationalGrouping.ElementSelector; if (node.Arguments.Count == 2) { selector = node.Arguments[1].UnwrapLambda().ExpandParameters(selector); } if (!(translatabilityAnalyzingExpressionVisitor.Visit(selector) is TranslatableExpression)) { break; } return(new SqlAggregateExpression( node.Method.Name == nameof(Queryable.Average) ? "AVG" : node.Method.Name.ToUpperInvariant(), selector, node.Method.ReturnType, relationalGrouping.IsDistinct && node.Arguments.Count == 1)); } case nameof(Queryable.Count): case nameof(Queryable.LongCount): { var selector = relationalGrouping.ElementSelector; if (node.Arguments.Count == 2) { var predicate = node.Arguments[1].UnwrapLambda().ExpandParameters(selector); if (!(translatabilityAnalyzingExpressionVisitor.Visit(predicate) is TranslatableExpression)) { break; } selector = Expression.Condition( predicate, Expression.Constant(1, typeof(int?)), Expression.Constant(null, typeof(int?))); } return(new SqlAggregateExpression( node.Method.Name == nameof(Queryable.Count) ? "COUNT" : "COUNT_BIG", selector.Type.IsScalarType() ? selector : new SqlFragmentExpression("*", selector.Type), node.Method.ReturnType, relationalGrouping.IsDistinct && node.Arguments.Count == 1)); } case nameof(Queryable.Distinct): { return(new GroupByResultExpression( relationalGrouping.SelectExpression, relationalGrouping.OuterKeySelector, relationalGrouping.InnerKeySelector, relationalGrouping.InnerKeyLambda, relationalGrouping.ElementSelector, true)); } case nameof(Queryable.Select): { var selectorLambda = node.Arguments[1].UnwrapLambda(); if (selectorLambda.Parameters.Count == 2) { // index parameter not supported break; } var selectorBody = selectorLambda .ExpandParameters(relationalGrouping.ElementSelector); if (!(translatabilityAnalyzingExpressionVisitor.Visit(selectorBody) is TranslatableExpression)) { break; } return(new GroupByResultExpression( relationalGrouping.SelectExpression, relationalGrouping.OuterKeySelector, relationalGrouping.InnerKeySelector, relationalGrouping.InnerKeyLambda, selectorBody, relationalGrouping.IsDistinct)); } } } return(node.Update(@object, arguments)); }
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); }
protected override Expression VisitMethodCall(MethodCallExpression node) { if (node.Method.IsQueryableOrEnumerableMethod() && !node.ContainsNonLambdaDelegates()) { switch (node.Method.Name) { case nameof(Queryable.GroupJoin): case nameof(Queryable.Join): { var arguments = Visit(node.Arguments).ToArray(); var genericArguments = node.Method.GetGenericArguments(); var outerKeySelector = arguments[2].UnwrapLambda(); var innerKeySelector = arguments[3].UnwrapLambda(); if (CanRewrite(outerKeySelector?.Body) && CanRewrite(innerKeySelector?.Body)) { outerKeySelector = Expression.Lambda( TryReduceNavigationKey(outerKeySelector.Body, out var rewroteOuter), outerKeySelector.Parameters[0]); innerKeySelector = Expression.Lambda( TryReduceNavigationKey(innerKeySelector.Body, out var rewroteInner), innerKeySelector.Parameters[0]); if (!rewroteOuter || !rewroteInner) { var primaryKeyDescriptor = descriptorSet .PrimaryKeyDescriptors .FirstOrDefault(d => d.TargetType.IsAssignableFrom(genericArguments[2])); if (primaryKeyDescriptor != null) { if (!rewroteOuter) { outerKeySelector = Expression.Lambda( primaryKeyDescriptor.KeySelector.ExpandParameters(outerKeySelector.Body), outerKeySelector.Parameters[0]); } if (!rewroteInner) { innerKeySelector = Expression.Lambda( primaryKeyDescriptor.KeySelector.ExpandParameters(innerKeySelector.Body), innerKeySelector.Parameters[0]); } } } if (outerKeySelector.ReturnType != innerKeySelector.ReturnType) { if (outerKeySelector.ReturnType.IsNullableType()) { innerKeySelector = Expression.Lambda( Expression.Convert(innerKeySelector.Body, outerKeySelector.ReturnType), innerKeySelector.Parameters); } else { outerKeySelector = Expression.Lambda( Expression.Convert(outerKeySelector.Body, innerKeySelector.ReturnType), outerKeySelector.Parameters); } } genericArguments[2] = outerKeySelector.ReturnType; if (node.Method.IsQueryableMethod()) { arguments[2] = Expression.Quote(outerKeySelector); arguments[3] = Expression.Quote(innerKeySelector); } else { arguments[2] = outerKeySelector; arguments[3] = innerKeySelector; } return(Expression.Call( node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArguments), arguments)); } break; } case nameof(Queryable.Contains): { var sequenceType = node.Arguments[0].Type.GetSequenceType(); var primaryKeyDescriptor = descriptorSet .PrimaryKeyDescriptors .FirstOrDefault(d => d.TargetType.IsAssignableFrom(sequenceType)); if (primaryKeyDescriptor is null || !primaryKeyDescriptor.KeySelector.ReturnType.IsScalarType()) { break; } var keyType = primaryKeyDescriptor.KeySelector.ReturnType; var arguments = Visit(node.Arguments).ToArray(); var setSelector = (Expression)primaryKeyDescriptor.KeySelector; var selectMethod = enumerableSelect.MakeGenericMethod(sequenceType, keyType); if (node.Method.IsQueryableMethod()) { setSelector = Expression.Quote(setSelector); selectMethod = queryableSelect.MakeGenericMethod(sequenceType, keyType); } return(Expression.Call( node.Method.GetGenericMethodDefinition().MakeGenericMethod(keyType), Expression.Call(selectMethod, arguments[0], setSelector), primaryKeyDescriptor.KeySelector.ExpandParameters(arguments[1]))); } } } return(base.VisitMethodCall(node)); }
protected override Expression VisitMethodCall(MethodCallExpression node) { if ((node.Method.DeclaringType == typeof(Queryable) || node.Method.DeclaringType == typeof(Enumerable)) && !node.ContainsNonLambdaDelegates()) { switch (node.Method.Name) { case nameof(Queryable.GroupJoin): case nameof(Queryable.Join): { var arguments = Visit(node.Arguments).ToArray(); var genericArguments = node.Method.GetGenericArguments(); var outerKeySelector = arguments[2].UnwrapLambda(); var innerKeySelector = arguments[3].UnwrapLambda(); if (CanRewrite(outerKeySelector.Body) && CanRewrite(innerKeySelector.Body)) { arguments[2] = Expression.Lambda( TryReduceNavigationKey(outerKeySelector.Body, out var rewroteOuter), outerKeySelector.Parameters[0]); arguments[3] = Expression.Lambda( TryReduceNavigationKey(innerKeySelector.Body, out var rewroteInner), innerKeySelector.Parameters[0]); if (!rewroteOuter || !rewroteInner) { var primaryKeyDescriptor = primaryKeyDescriptors .FirstOrDefault(d => d.TargetType.IsAssignableFrom(genericArguments[2])); if (primaryKeyDescriptor != null) { if (!rewroteOuter) { arguments[2] = Expression.Lambda( primaryKeyDescriptor.KeySelector.ExpandParameters(outerKeySelector.Body), outerKeySelector.Parameters[0]); } if (!rewroteInner) { arguments[3] = Expression.Lambda( primaryKeyDescriptor.KeySelector.ExpandParameters(innerKeySelector.Body), innerKeySelector.Parameters[0]); } } } if (node.Method.DeclaringType == typeof(Queryable)) { arguments[2] = Expression.Quote(arguments[2]); arguments[3] = Expression.Quote(arguments[3]); } genericArguments[2] = arguments[2].UnwrapLambda().ReturnType; return(Expression.Call( node.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArguments), arguments)); } break; } } } return(base.VisitMethodCall(node)); }