protected override Expression VisitMethodCall(MethodCallExpression methodCall) { // TODO: Handle List<> { // 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 (methodCall.Method.IsClosedFormOf(EnumerableAnyWithPredicate) && methodCall.Arguments[0].Type.IsArray && methodCall.Arguments[1] is LambdaExpression wherePredicate && wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && ( wherePredicateMethodCall.Method == Like2MethodInfo || wherePredicateMethodCall.Method == ILike2MethodInfo)) { var array = (SqlExpression)Visit(methodCall.Arguments[0]); var match = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]); return(_sqlExpressionFactory.ArrayAnyAll(match, array, ArrayComparisonType.Any, wherePredicateMethodCall.Method == Like2MethodInfo ? "LIKE" : "ILIKE")); } } // Same for All (but without the normalization { if (methodCall.Method.IsClosedFormOf(EnumerableAll) && methodCall.Arguments[0].Type.IsArray && methodCall.Arguments[1] is LambdaExpression wherePredicate && wherePredicate.Body is MethodCallExpression wherePredicateMethodCall && ( wherePredicateMethodCall.Method == Like2MethodInfo || wherePredicateMethodCall.Method == ILike2MethodInfo)) { var array = (SqlExpression)Visit(methodCall.Arguments[0]); var match = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]); return(_sqlExpressionFactory.ArrayAnyAll(match, array, ArrayComparisonType.All, wherePredicateMethodCall.Method == Like2MethodInfo ? "LIKE" : "ILIKE")); } } // Note: we also handle the above with equality instead of Like, see NpgsqlArrayMethodTranslator return(base.VisitMethodCall(methodCall)); }
/// <summary> /// Identifies complex array-related constructs which cannot be translated in regular method translators, since /// they require accessing lambdas. /// </summary> protected virtual Expression VisitArrayMethodCall(MethodInfo method, 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 match = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[1]); return(_sqlExpressionFactory.ArrayAnyAll(match, array, ArrayComparisonType.Any, wherePredicateMethodCall.Method == Like2MethodInfo ? "LIKE" : "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.ArrayAnyAll(match, array, ArrayComparisonType.All, wherePredicateMethodCall.Method == Like2MethodInfo ? "LIKE" : "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.IsArray && wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression && parameterExpression == wherePredicate.Parameters[0]) { var array1 = (SqlExpression)Visit(arguments[0]); var array2 = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0]); var inferredMapping = ExpressionExtensions.InferTypeMapping(array1, array2); return(new SqlCustomBinaryExpression( _sqlExpressionFactory.ApplyTypeMapping(array1, inferredMapping), _sqlExpressionFactory.ApplyTypeMapping(array2, inferredMapping), "&&", typeof(bool), _boolMapping)); } } { // 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.IsArray && wherePredicateMethodCall.Arguments[1] is ParameterExpression parameterExpression && parameterExpression == wherePredicate.Parameters[0]) { var array1 = (SqlExpression)Visit(arguments[0]); var array2 = (SqlExpression)Visit(wherePredicateMethodCall.Arguments[0]); var inferredMapping = ExpressionExtensions.InferTypeMapping(array1, array2); return(new SqlCustomBinaryExpression( _sqlExpressionFactory.ApplyTypeMapping(array1, inferredMapping), _sqlExpressionFactory.ApplyTypeMapping(array2, inferredMapping), "<@", typeof(bool), _boolMapping)); } } return(null); }