diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryProjectionBindingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryProjectionBindingExpressionVisitor.cs index 4ac33316af2..3b85b524b31 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryProjectionBindingExpressionVisitor.cs @@ -237,9 +237,8 @@ protected override Expression VisitExtension(Expression extensionExpression) EntityProjectionExpression entityProjectionExpression; if (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression) { - VerifyQueryExpression(projectionBindingExpression); - entityProjectionExpression = (EntityProjectionExpression)_queryExpression.GetMappedProjection( - projectionBindingExpression.ProjectionMember); + entityProjectionExpression = (EntityProjectionExpression)((InMemoryQueryExpression)projectionBindingExpression.QueryExpression) + .GetMappedProjection(projectionBindingExpression.ProjectionMember); } else { @@ -486,15 +485,6 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) : unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type)); } - // TODO: Debugging - private void VerifyQueryExpression(ProjectionBindingExpression projectionBindingExpression) - { - if (projectionBindingExpression.QueryExpression != _queryExpression) - { - throw new InvalidOperationException(CoreStrings.QueryFailed(projectionBindingExpression.Print(), GetType().Name)); - } - } - private CollectionShaperExpression AddCollectionProjection( ShapedQueryExpression subquery, INavigationBase navigation, diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs index 79907a9b2fc..f245bff2b3a 100644 --- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs @@ -138,8 +138,8 @@ public override Expression Visit(Expression expression) if (projectionBindingExpression.ProjectionMember != null) { // This would be SqlExpression. EntityProjectionExpression would be wrapped inside EntityShaperExpression. - var mappedProjection = (SqlExpression)_selectExpression.GetMappedProjection( - projectionBindingExpression.ProjectionMember); + var mappedProjection = (SqlExpression)((SelectExpression)projectionBindingExpression.QueryExpression) + .GetMappedProjection(projectionBindingExpression.ProjectionMember); return new ProjectionBindingExpression( _selectExpression, _selectExpression.AddToProjection(mappedProjection), expression.Type); @@ -299,7 +299,6 @@ protected override Expression VisitExtension(Expression extensionExpression) EntityProjectionExpression entityProjectionExpression; if (entityShaperExpression.ValueBufferExpression is ProjectionBindingExpression projectionBindingExpression) { - VerifySelectExpression(projectionBindingExpression); // If projectionBinding is not mapped then SelectExpression has client projection // Hence force client eval if (projectionBindingExpression.ProjectionMember == null) @@ -318,8 +317,8 @@ protected override Expression VisitExtension(Expression extensionExpression) return null; } - entityProjectionExpression = (EntityProjectionExpression)_selectExpression.GetMappedProjection( - projectionBindingExpression.ProjectionMember); + entityProjectionExpression = (EntityProjectionExpression)((SelectExpression)projectionBindingExpression.QueryExpression) + .GetMappedProjection(projectionBindingExpression.ProjectionMember); } else { @@ -570,15 +569,6 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) : unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type)); } - // TODO: Debugging - private void VerifySelectExpression(ProjectionBindingExpression projectionBindingExpression) - { - if (projectionBindingExpression.QueryExpression != _selectExpression) - { - throw new InvalidOperationException(CoreStrings.QueryFailed(projectionBindingExpression.Print(), GetType().Name)); - } - } - private static Expression MatchTypes(Expression expression, Type targetType) { if (targetType != expression.Type diff --git a/test/EFCore.Specification.Tests/Query/NorthwindJoinQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindJoinQueryTestBase.cs index c5290205555..05a4e992eef 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindJoinQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindJoinQueryTestBase.cs @@ -878,5 +878,27 @@ private bool Equals(OrderDetailViewModel orderDetailViewModel) public override int GetHashCode() => HashCode.Combine(_orderID, _productID); } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_with_selecting_outer_entity(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .SelectMany(c => c.Orders.Select(o => c)), + entryCount: 89); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task SelectMany_with_selecting_outer_element(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Select(e => new { e, Complex = e.CustomerID + e.City }) + .SelectMany(c => c.e.Orders.Select(o => c.Complex))); + } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs index f5bf5fdc1fc..19f46a28f9c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindJoinQuerySqlServerTest.cs @@ -583,6 +583,34 @@ WHERE [c].[CustomerID] LIKE N'A%' ORDER BY [c].[CustomerID], [t0].[OrderID0], [t0].[OrderID1], [t0].[ProductID0]"); } + public override async Task SelectMany_with_selecting_outer_entity(bool async) + { + await base.SelectMany_with_selecting_outer_entity(async); + + AssertSql( + @"SELECT [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] +FROM [Customers] AS [c] +CROSS APPLY ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] +) AS [t]"); + } + + public override async Task SelectMany_with_selecting_outer_element(bool async) + { + await base.SelectMany_with_selecting_outer_element(async); + + AssertSql( + @"SELECT [t].[c] +FROM [Customers] AS [c] +CROSS APPLY ( + SELECT [c].[CustomerID] + COALESCE([c].[City], N'') AS [c] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] +) AS [t]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindJoinQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindJoinQuerySqliteTest.cs index a6ca224a717..65865145642 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindJoinQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindJoinQuerySqliteTest.cs @@ -36,5 +36,17 @@ public override async Task SelectMany_with_client_eval_with_collection_shaper_ig SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.SelectMany_with_client_eval_with_collection_shaper_ignored(async))).Message); + + public override async Task SelectMany_with_selecting_outer_entity(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.SelectMany_with_selecting_outer_entity(async))).Message); + + public override async Task SelectMany_with_selecting_outer_element(bool async) + => Assert.Equal( + SqliteStrings.ApplyNotSupported, + (await Assert.ThrowsAsync( + () => base.SelectMany_with_selecting_outer_element(async))).Message); } }