Example #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);
        }
Example #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];

            {
                // 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)));
                }