In QueryableMethodTranslatingExpressionVisitor, our operator dispatching logic first visits the source, and only if that visitation succeeds, proceeds to dispatch into one of the Translate methods. For example, the TranslateAny method looks like this:
protected abstract ShapedQueryExpression? TranslateAny(ShapedQueryExpression source, LambdaExpression? predicate);
Note that it accepts an already-translated source (ShapedQueryExpression), but an untranslated predicate (LambdaExpression).
This can be a problem in cases where we want to perform higher-level pattern matching involving the original, untranslated source expression, either because it's untranslatable on its own, or because the original expression is a better fit for pattern matching. A specific example is pattern matching Contains (TranslateAny) over a ParameterQueryRootExpression; the translation of that is provider-specific (e.g. OPENJSON for SQL Server), and so we can't identify it in the relational layer (#30712).
The proposed change here is simply to pass the original, untranslated expression to all the Translate* methods; each method would Visit that internally, and perform any matching if it needs to. This would be a provider-facing breaking change.
In QueryableMethodTranslatingExpressionVisitor, our operator dispatching logic first visits the source, and only if that visitation succeeds, proceeds to dispatch into one of the Translate methods. For example, the TranslateAny method looks like this:
Note that it accepts an already-translated source (ShapedQueryExpression), but an untranslated predicate (LambdaExpression).
This can be a problem in cases where we want to perform higher-level pattern matching involving the original, untranslated source expression, either because it's untranslatable on its own, or because the original expression is a better fit for pattern matching. A specific example is pattern matching Contains (TranslateAny) over a ParameterQueryRootExpression; the translation of that is provider-specific (e.g. OPENJSON for SQL Server), and so we can't identify it in the relational layer (#30712).
The proposed change here is simply to pass the original, untranslated expression to all the Translate* methods; each method would Visit that internally, and perform any matching if it needs to. This would be a provider-facing breaking change.