diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index f05d68fe555..1b92540c7af 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -220,6 +220,7 @@ protected override Expression VisitMember(MemberExpression memberExpression) { // This is FirstOrDefault.Member // due to SubqueryMemberPushdown, this may be collection navigation which was not pushed down + navigationExpansionExpression = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(navigationExpansionExpression); var expandedExpression = new ExpandingExpressionVisitor(this, navigationExpansionExpression).Visit(updatedExpression); if (expandedExpression != updatedExpression) { diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs index 12c4ae26e4f..451f3d548c3 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs @@ -4163,6 +4163,12 @@ public override Task Max_on_empty_sequence_throws(bool async) return base.Max_on_empty_sequence_throws(async); } + [ConditionalTheory(Skip = "Non embedded collection subquery Issue#17246")] + public override Task Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(bool async) + { + return base.Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(async); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs index c5b2ac18efe..de53fc59a08 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs @@ -6074,5 +6074,30 @@ public virtual async Task Max_on_empty_sequence_throws(bool async) Assert.Equal("Nullable object must have a value.", message); } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(bool async) + { + return AssertQuery( + async, + ss => ss.Set() + .Where(c => c.CustomerID.StartsWith("F")) + .OrderBy(c => c.CustomerID) + .Select(c => new + { + Complex = (bool?)c.Orders.OrderBy(e => e.OrderDate).FirstOrDefault().Customer.Orders.Any(e => e.OrderID < 11000) + }), + ss => ss.Set() + .Where(c => c.CustomerID.StartsWith("F")) + .OrderBy(c => c.CustomerID) + .Select(c => new + { + Complex = c.Orders.OrderBy(e => e.OrderDate).FirstOrDefault() != null + ? c.Orders.OrderBy(e => e.OrderDate).FirstOrDefault().Customer.Orders.Any(e => e.OrderID < 11000) + : (bool?)false + }), + assertOrder: true); + } } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs index a0031f14a35..cbb15b514af 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindMiscellaneousQuerySqlServerTest.cs @@ -5190,6 +5190,38 @@ FROM [Customers] AS [c] ORDER BY [c].[CustomerID]"); } + public override async Task Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(bool async) + { + await base.Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(async); + + AssertSql( + @"SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM [Orders] AS [o] + WHERE (( + SELECT TOP(1) [c].[CustomerID] + FROM [Orders] AS [o0] + LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID] + WHERE [c1].[CustomerID] = [o0].[CustomerID] + ORDER BY [o0].[OrderDate]) IS NOT NULL AND ((( + SELECT TOP(1) [c0].[CustomerID] + FROM [Orders] AS [o1] + LEFT JOIN [Customers] AS [c0] ON [o1].[CustomerID] = [c0].[CustomerID] + WHERE [c1].[CustomerID] = [o1].[CustomerID] + ORDER BY [o1].[OrderDate]) = [o].[CustomerID]) OR (( + SELECT TOP(1) [c0].[CustomerID] + FROM [Orders] AS [o1] + LEFT JOIN [Customers] AS [c0] ON [o1].[CustomerID] = [c0].[CustomerID] + WHERE [c1].[CustomerID] = [o1].[CustomerID] + ORDER BY [o1].[OrderDate]) IS NULL AND [o].[CustomerID] IS NULL))) AND ([o].[OrderID] < 11000)) THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END AS [Complex] +FROM [Customers] AS [c1] +WHERE [c1].[CustomerID] LIKE N'F%' +ORDER BY [c1].[CustomerID]"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);