Esempio n. 1
0
        /// <summary>
        /// Identifies complex array-related constructs which cannot be translated in regular method translators, since
        /// they require accessing lambdas.
        /// </summary>
        protected virtual Expression VisitArrayMethodCall(
            [NotNull] MethodInfo method, [NotNull] ReadOnlyCollection <Expression> arguments)
        {
            {
                // Pattern match for .Where(e => new[] { "a", "b", "c" }.Any(p => EF.Functions.Like(e.SomeText, p))),
                // which we translate to WHERE s.""SomeText"" LIKE ANY (ARRAY['a','b','c']) (see test Any_like)
                // Note: NavigationExpander normalized Any(x) to Where(x).Any()
                if (method.IsClosedFormOf(EnumerableAnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && (
                        wherePredicateMethodCall.Method == Like2MethodInfo ||
                        wherePredicateMethodCall.Method == ILike2MethodInfo))
                {
                    var array = (SqlExpression)Visit(arguments[0]);
                    var item  = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]);

                    return(_sqlExpressionFactory.Any(item, array,
                                                     wherePredicateMethodCall.Method == Like2MethodInfo
                            ? PostgresAnyOperatorType.Like : PostgresAnyOperatorType.ILike));
                }

                // Note: we also handle the above with equality instead of Like, see NpgsqlArrayMethodTranslator
            }

            {
                // Same for All (but without the normalization)
                if (method.IsClosedFormOf(EnumerableAll) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && (
                        wherePredicateMethodCall.Method == Like2MethodInfo ||
                        wherePredicateMethodCall.Method == ILike2MethodInfo))
                {
                    var array = (SqlExpression)Visit(arguments[0]);
                    var match = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]);

                    return(_sqlExpressionFactory.All(match, array,
                                                     wherePredicateMethodCall.Method == Like2MethodInfo
                            ? PostgresAllOperatorType.Like : PostgresAllOperatorType.ILike));
                }
            }

            {
                // Translate e => new[] { 4, 5 }.Any(p => e.SomeArray.Contains(p)),
                // using array overlap (&&)
                if (method.IsClosedFormOf(EnumerableAnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall &&
                    wherePredicateMethodCall.Method.IsClosedFormOf(Contains) &&
                    wherePredicateMethodCall.Arguments[0].Type.IsArrayOrGenericList() &&
                    wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression &&
                    parameterExpression == wherePredicate.Parameters[0])
                {
                    return(_sqlExpressionFactory.Overlaps(
                               (SqlExpression)Visit(arguments[0]),
                               (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0])));
                }
            }

            {
                // Translate e => new[] { 4, 5 }.All(p => e.SomeArray.Contains(p)),
                // using array containment (<@)
                if (method.IsClosedFormOf(EnumerableAll) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall &&
                    wherePredicateMethodCall.Method.IsClosedFormOf(Contains) &&
                    wherePredicateMethodCall.Arguments[0].Type.IsArrayOrGenericList() &&
                    wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression &&
                    parameterExpression == wherePredicate.Parameters[0])
                {
                    return(_sqlExpressionFactory.ContainedBy(
                               (SqlExpression)Visit(arguments[0]),
                               (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0])));
                }
            }

            return(null);
        }
Esempio n. 2
0
        /// <summary>
        /// Identifies complex array-related constructs which cannot be translated in regular method translators, since
        /// they require accessing lambdas.
        /// </summary>
        private Expression VisitArrayMethodCall([NotNull] MethodInfo method, [NotNull] ReadOnlyCollection <Expression> arguments)
        {
            var array = arguments[0];

            {
                if (method.IsClosedFormOf(EnumerableMethods.AnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate)
                {
                    if (wherePredicate.Body is MethodCallExpression wherePredicateMethodCall)
                    {
                        var predicateMethod    = wherePredicateMethodCall.Method;
                        var predicateArguments = wherePredicateMethodCall.Arguments;

                        // Pattern match: new[] { "a", "b", "c" }.Any(p => EF.Functions.Like(e.SomeText, p))
                        // Translation: s.SomeText LIKE ANY (ARRAY['a','b','c'])
                        // Note: we also handle the this equality instead of Like, see NpgsqlArrayMethodTranslator
                        if ((predicateMethod == Like2MethodInfo || predicateMethod == ILike2MethodInfo) &&
                            predicateArguments[2] == wherePredicate.Parameters[0])
                        {
                            return(_sqlExpressionFactory.Any(
                                       (SqlExpression)Visit(predicateArguments[1]),
                                       (SqlExpression)Visit(array),
                                       wherePredicateMethodCall.Method == Like2MethodInfo
                                    ? PostgresAnyOperatorType.Like
                                    : PostgresAnyOperatorType.ILike));
                        }

                        // Pattern match: new[] { 4, 5 }.Any(p => e.SomeArray.Contains(p))
                        // Translation: s.SomeArray && ARRAY[4, 5] (array overlap).
                        if (predicateMethod.IsClosedFormOf(EnumerableMethods.Contains) &&
                            predicateArguments[0].Type.IsArrayOrGenericList() &&
                            predicateArguments[1] is ParameterExpression parameterExpression1 &&
                            parameterExpression1 == wherePredicate.Parameters[0])
                        {
                            return(_sqlExpressionFactory.Overlaps(
                                       (SqlExpression)Visit(arguments[0]),
                                       (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0])));
                        }

                        // As above, but for Contains on List<T>
                        if (predicateMethod.DeclaringType?.IsGenericType == true &&
                            predicateMethod.DeclaringType.GetGenericTypeDefinition() == typeof(List <>) &&
                            predicateMethod.Name == nameof(List <int> .Contains) &&
                            predicateMethod.GetParameters().Length == 1 &&
                            predicateArguments[0] is ParameterExpression parameterExpression2 &&
                            parameterExpression2 == wherePredicate.Parameters[0])
                        {
                            return(_sqlExpressionFactory.Overlaps(
                                       (SqlExpression)Visit(arguments[0]),
                                       (SqlExpression)Visit(wherePredicateMethodCall.Object)));
                        }
                    }

                    // Pattern match for: array.Any(e => e == x) (and other equality patterns)
                    // Transform this to Contains.
                    if (TryMatchEquality(wherePredicate.Body, out var left, out var right) &&
                        (left == wherePredicate.Parameters[0] || right == wherePredicate.Parameters[0]))
                    {
                        var item = left == wherePredicate.Parameters[0]
                            ? right
                            : right == wherePredicate.Parameters[0]
                                ? left
                                : null;

                        return(item is null
                            ? null
                            : Visit(Expression.Call(EnumerableMethods.Contains.MakeGenericMethod(method.GetGenericArguments()[0]), array,
                                                    item)));
                    }
Esempio n. 3
0
        /// <summary>
        /// Identifies complex array-related constructs which cannot be translated in regular method translators, since
        /// they require accessing lambdas.
        /// </summary>
        private Expression VisitArrayMethodCall(
            [NotNull] MethodInfo method, [NotNull] ReadOnlyCollection <Expression> arguments)
        {
            var array = arguments[0];

            {
                // Pattern match for: new[] { "a", "b", "c" }.Any(p => EF.Functions.Like(e.SomeText, p)),
                // which we translate to WHERE s.""SomeText"" LIKE ANY (ARRAY['a','b','c']) (see test Any_like)
                // Note: NavigationExpander normalized Any(x) to Where(x).Any()
                if (method.IsClosedFormOf(EnumerableMethods.AnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && (
                        wherePredicateMethodCall.Method == Like2MethodInfo ||
                        wherePredicateMethodCall.Method == ILike2MethodInfo) &&
                    wherePredicateMethodCall.Arguments is var whereArguments &&
                    whereArguments[2] == wherePredicate.Parameters[0])
                {
                    return(_sqlExpressionFactory.Any(
                               (SqlExpression)Visit(whereArguments[1]),
                               (SqlExpression)Visit(array),
                               wherePredicateMethodCall.Method == Like2MethodInfo
                            ? PostgresAnyOperatorType.Like : PostgresAnyOperatorType.ILike));
                }

                // Note: we also handle the above with equality instead of Like, see NpgsqlArrayMethodTranslator
            }

            {
                // Same for All (but without the normalization)
                if (method.IsClosedFormOf(EnumerableMethods.All) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && (
                        wherePredicateMethodCall.Method == Like2MethodInfo ||
                        wherePredicateMethodCall.Method == ILike2MethodInfo) &&
                    wherePredicateMethodCall.Arguments is var whereArguments &&
                    whereArguments[2] == wherePredicate.Parameters[0])
                {
                    return(_sqlExpressionFactory.All(
                               (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]),
                               (SqlExpression)Visit(arguments[0]),
                               wherePredicateMethodCall.Method == Like2MethodInfo
                            ? PostgresAllOperatorType.Like : PostgresAllOperatorType.ILike));
                }
            }

            {
                if (method.IsClosedFormOf(EnumerableMethods.AnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall)
                {
                    var predicateMethod = wherePredicateMethodCall.Method;

                    // Pattern match for: new[] { 4, 5 }.Any(p => e.SomeArray.Contains(p)),
                    // using array overlap (&&).
                    if (predicateMethod.IsClosedFormOf(EnumerableMethods.Contains) &&
                        wherePredicateMethodCall.Arguments[0].Type.IsArrayOrGenericList() &&
                        wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression1 &&
                        parameterExpression1 == wherePredicate.Parameters[0])
                    {
                        return(_sqlExpressionFactory.Overlaps(
                                   (SqlExpression)Visit(arguments[0]),
                                   (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0])));
                    }

                    // As above, but for Contains on List<T>
                    if (predicateMethod.DeclaringType?.IsGenericType == true &&
                        predicateMethod.DeclaringType.GetGenericTypeDefinition() == typeof(List <>) &&
                        predicateMethod.Name == nameof(List <int> .Contains) &&
                        predicateMethod.GetParameters().Length == 1 &&
                        wherePredicateMethodCall.Arguments[0] is ParameterExpression parameterExpression2 &&
                        parameterExpression2 == wherePredicate.Parameters[0])
                    {
                        return(_sqlExpressionFactory.Overlaps(
                                   (SqlExpression)Visit(arguments[0]),
                                   (SqlExpression)Visit(wherePredicateMethodCall.Object)));
                    }
                }
            }

            {
                // Pattern match for: new[] { 4, 5 }.All(p => e.SomeArray.Contains(p)),
                // using array containment (<@)
                if (method.IsClosedFormOf(EnumerableMethods.All) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    wherePredicate.Body is MethodCallExpression wherePredicateMethodCall &&
                    wherePredicateMethodCall.Method.IsClosedFormOf(EnumerableMethods.Contains) &&
                    wherePredicateMethodCall.Arguments[0].Type.IsArrayOrGenericList() &&
                    wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression &&
                    parameterExpression == wherePredicate.Parameters[0])
                {
                    return(_sqlExpressionFactory.ContainedBy(
                               (SqlExpression)Visit(arguments[0]),
                               (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0])));
                }
            }

            {
                // Pattern match for: array.Any(e => e == x) (and other equality patterns)
                // Transform this to Contains.
                if (method.IsClosedFormOf(EnumerableMethods.AnyWithPredicate) &&
                    arguments[1] is LambdaExpression wherePredicate &&
                    TryMatchEquality(wherePredicate.Body, out var left, out var right) &&
                    (left == wherePredicate.Parameters[0] || right == wherePredicate.Parameters[0]))
                {
                    var item = left == wherePredicate.Parameters[0]
                        ? right
                        : right == wherePredicate.Parameters[0]
                            ? left
                            : null;

                    return(item is null
                        ? null
                        : Visit(Expression.Call(EnumerableMethods.Contains.MakeGenericMethod(method.GetGenericArguments()[0]), array, item)));
                }