Exemple #1
0
        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));
        }