From 11bd40551187c078ed64c049b237ac14ce5668a9 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 27 Jun 2019 09:14:07 -0700 Subject: [PATCH] Query: Throw for unknown method encountered in QueryableMethodTranslator Except for the subquery case This visitor should only translate methods on Queryable so it should not do base eval for others. All other methods should be passed through SqlTranslator (as they appear inside a lambda) For subquery, we don't want to throw since we don't know if the subquery has client methods or not (projection case) This is required for supporting collections/single non-scalar in projection. --- ...osQueryableMethodTranslatingExpressionVisitor.cs | 1 + ...ryQueryableMethodTranslatingExpressionVisitor.cs | 1 + ...alQueryableMethodTranslatingExpressionVisitor.cs | 2 ++ .../QueryableMethodTranslatingExpressionVisitor.cs | 13 ++++++++++--- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/EFCore.Cosmos/Query/Pipeline/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Pipeline/CosmosQueryableMethodTranslatingExpressionVisitor.cs index 3d3f094f62f..cf1fbb59806 100644 --- a/src/EFCore.Cosmos/Query/Pipeline/CosmosQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Pipeline/CosmosQueryableMethodTranslatingExpressionVisitor.cs @@ -23,6 +23,7 @@ public CosmosQueryableMethodTranslatingExpressionVisitor( ISqlExpressionFactory sqlExpressionFactory, IMemberTranslatorProvider memberTranslatorProvider, IMethodCallTranslatorProvider methodCallTranslatorProvider) + : base(subquery: false) { _model = model; _sqlExpressionFactory = sqlExpressionFactory; diff --git a/src/EFCore.InMemory/Query/Pipeline/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Pipeline/InMemoryQueryableMethodTranslatingExpressionVisitor.cs index c3dcfe12c75..f8d42811c9a 100644 --- a/src/EFCore.InMemory/Query/Pipeline/InMemoryQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Pipeline/InMemoryQueryableMethodTranslatingExpressionVisitor.cs @@ -18,6 +18,7 @@ public class InMemoryQueryableMethodTranslatingExpressionVisitor : QueryableMeth private readonly IModel _model; public InMemoryQueryableMethodTranslatingExpressionVisitor(IModel model) + : base(subquery: false) { _expressionTranslator = new InMemoryExpressionTranslatingExpressionVisitor(); _projectionBindingExpressionVisitor = new InMemoryProjectionBindingExpressionVisitor(_expressionTranslator); diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs index bd0d0648528..33660c3a5b3 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -28,6 +28,7 @@ public RelationalQueryableMethodTranslatingExpressionVisitor( IModel model, IRelationalSqlTranslatingExpressionVisitorFactory relationalSqlTranslatingExpressionVisitorFactory, ISqlExpressionFactory sqlExpressionFactory) + : base(subquery: false) { _sqlTranslator = relationalSqlTranslatingExpressionVisitorFactory.Create(model, this); _projectionBindingExpressionVisitor = new RelationalProjectionBindingExpressionVisitor(this, _sqlTranslator); @@ -39,6 +40,7 @@ private RelationalQueryableMethodTranslatingExpressionVisitor( IModel model, RelationalSqlTranslatingExpressionVisitor sqlTranslator, ISqlExpressionFactory sqlExpressionFactory) + : base(subquery: true) { _model = model; _sqlTranslator = sqlTranslator; diff --git a/src/EFCore/Query/Pipeline/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/QueryableMethodTranslatingExpressionVisitor.cs index 984be1e5107..26a24d54b62 100644 --- a/src/EFCore/Query/Pipeline/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/QueryableMethodTranslatingExpressionVisitor.cs @@ -13,6 +13,13 @@ namespace Microsoft.EntityFrameworkCore.Query.Pipeline { public abstract class QueryableMethodTranslatingExpressionVisitor : ExpressionVisitor { + private readonly bool _subquery; + + protected QueryableMethodTranslatingExpressionVisitor(bool subquery) + { + _subquery = subquery; + } + protected override Expression VisitConstant(ConstantExpression constantExpression) => constantExpression.IsEntityQueryable() ? CreateShapedQueryExpression(((IQueryable)constantExpression.Value).ElementType) @@ -424,8 +431,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp break; } } - - throw new NotImplementedException("Unhandled method: " + methodCallExpression.Method.Name); } // TODO: Skip ToOrderedQueryable method. See Issue#15591 @@ -435,7 +440,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return Visit(methodCallExpression.Arguments[0]); } - return base.VisitMethodCall(methodCallExpression); + return _subquery + ? (Expression)null + : throw new NotImplementedException("Unhandled method: " + methodCallExpression.Method.Name); } protected Type CreateTransparentIdentifierType(Type outerType, Type innerType)