From 7e30f114244d5d6ad095e1e26ed4ded44c15779c Mon Sep 17 00:00:00 2001 From: Henrique Ruschel Date: Sun, 19 Apr 2026 19:23:10 -0300 Subject: [PATCH] fix for GroupBy.Select EmptyProjectionMember --- ...lationalSqlTranslatingExpressionVisitor.cs | 2 +- .../NorthwindGroupByQueryInMemoryTest.cs | 8 +++ .../Query/NorthwindGroupByQueryTestBase.cs | 35 ++++++++++ .../NorthwindGroupByQuerySqlServerTest.cs | 70 +++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 4242f97d5a8..5e71678cdef 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -1348,7 +1348,7 @@ private SqlExpression BindProperty(StructuralTypeReferenceExpression typeReferen case { Subquery: { } subquery }: { var entityShaper = (StructuralTypeShaperExpression)subquery.ShaperExpression; - var subSelectExpression = (SelectExpression)subquery.QueryExpression; + var subSelectExpression = ((SelectExpression)subquery.QueryExpression).Clone(); var projectionBindingExpression = (ProjectionBindingExpression)entityShaper.ValueBufferExpression; var projection = (StructuralTypeProjectionExpression)subSelectExpression.GetProjection(projectionBindingExpression); diff --git a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindGroupByQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindGroupByQueryInMemoryTest.cs index 1e7af050b34..5f07540e564 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindGroupByQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindGroupByQueryInMemoryTest.cs @@ -72,4 +72,12 @@ public override Task Final_GroupBy_TagWith(bool async) => AssertTranslationFailedWithDetails( () => base.Final_GroupBy_TagWith(async), InMemoryStrings.NonComposedGroupByNotSupported); + + [ConditionalTheory(Skip = "Issue#31209")] + public override Task GroupBy_Select_Entire_Entity_Order(bool async) + => base.GroupBy_Select_Entire_Entity_Order(async); + + [ConditionalTheory(Skip = "Issue#31209")] + public override Task GroupBy_Select_Entire_Entity_Where(bool async) + => base.GroupBy_Select_Entire_Entity_Where(async); } diff --git a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs index 9cf62879be5..48e5861be1f 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindGroupByQueryTestBase.cs @@ -1949,6 +1949,41 @@ public virtual Task GroupBy_aggregate_join_with_grouping_key(bool async) AssertEqual(e.Count, a.Count); }); + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_Select_Entire_Entity_Where(bool async) // #31209 + => AssertQuery( + async, + ss => ss.Set().GroupBy(o => o.CustomerID) + .Select(a => a.First()) + .Where(x => x.EmployeeID == 6u)); + + [ConditionalTheory(Skip = "Issue#31209"), MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_Select_Entire_Entity_Where_Select(bool async) // #31209 + => AssertQuery( + async, + ss => ss.Set().GroupBy(x => x.OrderID) + .Select(x => x.First()) + .Where(x => x.OrderID > 10) + .Select(r => r.EmployeeID)); + + [ConditionalTheory(Skip = "Issue#31209"), MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_Select_Entire_Entity_Select(bool async) // #31209 + => AssertQuery( + async, + ss => ss.Set().GroupBy(x => x.OrderID) + .Select(x => x.First()) + .Select(r => r.EmployeeID)); + + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public virtual Task GroupBy_Select_Entire_Entity_Order(bool async) // #31209 + => AssertQuery( + async, + ss => ss.Set().GroupBy(o => o.CustomerID) + .Select(a => a.First()) + .OrderBy(x => x.EmployeeID) + .ThenBy(x => x.OrderID), + assertOrder: true); + [ConditionalTheory, MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_aggregate_join_with_group_result(bool async) => AssertQuery( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index 53a0312be1c..2ae0f2ba36e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -3056,6 +3056,76 @@ GROUP BY [o].[CustomerID] """); } + public override async Task GroupBy_Select_Entire_Entity_Where(bool async) + { + await base.GroupBy_Select_Entire_Entity_Where(async); + + AssertSql( +""" +SELECT [o4].[OrderID], [o4].[CustomerID], [o4].[EmployeeID], [o4].[OrderDate] +FROM ( + SELECT [o].[CustomerID] + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] + HAVING ( + SELECT TOP(1) [o1].[EmployeeID] + FROM [Orders] AS [o1] + WHERE [o].[CustomerID] = [o1].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o1].[CustomerID] IS NULL)) = 6 +) AS [o2] +LEFT JOIN ( + SELECT [o3].[OrderID], [o3].[CustomerID], [o3].[EmployeeID], [o3].[OrderDate] + FROM ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o0].[CustomerID] ORDER BY [o0].[OrderID]) AS [row] + FROM [Orders] AS [o0] + ) AS [o3] + WHERE [o3].[row] <= 1 +) AS [o4] ON [o2].[CustomerID] = [o4].[CustomerID] +"""); + } + + public override async Task GroupBy_Select_Entire_Entity_Where_Select(bool async) + { + await base.GroupBy_Select_Entire_Entity_Where_Select(async); + + AssertSql(); + } + + public override async Task GroupBy_Select_Entire_Entity_Select(bool async) + { + await base.GroupBy_Select_Entire_Entity_Select(async); + + AssertSql(); + } + + public override async Task GroupBy_Select_Entire_Entity_Order(bool async) + { + await base.GroupBy_Select_Entire_Entity_Order(async); + + AssertSql( +""" +SELECT [o5].[OrderID], [o5].[CustomerID], [o5].[EmployeeID], [o5].[OrderDate] +FROM ( + SELECT [o].[CustomerID], ( + SELECT TOP(1) [o1].[EmployeeID] + FROM [Orders] AS [o1] + WHERE [o].[CustomerID] = [o1].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o1].[CustomerID] IS NULL)) AS [c], ( + SELECT TOP(1) [o2].[OrderID] + FROM [Orders] AS [o2] + WHERE [o].[CustomerID] = [o2].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o2].[CustomerID] IS NULL)) AS [c0] + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] +) AS [o3] +LEFT JOIN ( + SELECT [o4].[OrderID], [o4].[CustomerID], [o4].[EmployeeID], [o4].[OrderDate] + FROM ( + SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o0].[CustomerID] ORDER BY [o0].[OrderID]) AS [row] + FROM [Orders] AS [o0] + ) AS [o4] + WHERE [o4].[row] <= 1 +) AS [o5] ON [o3].[CustomerID] = [o5].[CustomerID] +ORDER BY [o3].[c], [o3].[c0] +"""); + } public override async Task GroupBy_aggregate_join_with_group_result(bool async) { await base.GroupBy_aggregate_join_with_group_result(async);