From d0284cba7f45d542f3460f8928bc4a819992ec68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:33:18 +0000 Subject: [PATCH 1/3] Initial plan From a8d39ffb7a0bc5ab4948681068420184825c0325 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:36:21 +0000 Subject: [PATCH 2/3] Document better SQL for to-one joins Document https://github.com/dotnet/efcore/pull/37819 Co-authored-by: roji <1862641+roji@users.noreply.github.com> --- .../core/what-is-new/ef-core-11.0/whatsnew.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md index a519703cec..29906e7e5f 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md @@ -128,6 +128,68 @@ WHERE JSON_PATH_EXISTS([b].[JsonData], N'$.OptionalInt') = 1 For the full `JSON_PATH_EXISTS` SQL Server documentation, see [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql). + + +### Better SQL for to-one joins + +EF Core 11 generates better SQL when querying with to-one (reference) navigation includes in two ways. + +First, when using split queries (`AsSplitQuery()`), EF previously added unnecessary joins to reference navigations in the SQL generated for collection queries. For example, consider the following query: + +```csharp +var blogs = context.Blogs + .Include(b => b.BlogType) + .Include(b => b.Posts) + .AsSplitQuery() + .ToList(); +``` + +EF Core previously generated a split query for `Posts` that unnecessarily joined `BlogType`: + +```sql +-- Before EF Core 11 +SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id], [b0].[Id] +FROM [Blogs] AS [b] +INNER JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] = [b0].[Id] +INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId] +ORDER BY [b].[Id], [b0].[Id] +``` + +In EF Core 11, the unneeded join is pruned: + +```sql +-- EF Core 11 +SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id] +FROM [Blogs] AS [b] +INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId] +ORDER BY [b].[Id] +``` + +Second, EF no longer adds redundant keys from reference navigations to `ORDER BY` clauses. Because a reference navigation's key is functionally determined by the parent entity's key (via the foreign key), it does not need to appear separately. For example: + +```csharp +var blogs = context.Blogs + .Include(b => b.Owner) + .Include(b => b.Posts) + .ToList(); +``` + +EF Core previously included `[p].[PersonId]` in the `ORDER BY`, even though `[b].[BlogId]` already uniquely identifies the row: + +```sql +-- Before EF Core 11 +ORDER BY [b].[BlogId], [p].[PersonId] +``` + +In EF Core 11, the redundant column is omitted: + +```sql +-- EF Core 11 +ORDER BY [b].[BlogId] +``` + +Both optimizations can have a significant positive impact on query performance, especially when multiple reference navigations are included. + ## Cosmos DB From b06d632a14b8ec111be48b0ed951f00b68845807 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:45:26 +0000 Subject: [PATCH 3/3] Move "Better SQL for to-one joins" to first position under LINQ and SQL translation Co-authored-by: roji <1862641+roji@users.noreply.github.com> --- .../core/what-is-new/ef-core-11.0/whatsnew.md | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md index 29906e7e5f..fc5ab2ed0c 100644 --- a/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md +++ b/entity-framework/core/what-is-new/ef-core-11.0/whatsnew.md @@ -79,55 +79,6 @@ For more information on inheritance mapping strategies, see [Inheritance](xref:c ## LINQ and SQL translation - - -### MaxBy and MinBy - -EF Core now supports translating the LINQ `MaxByAsync` and `MinByAsync` methods (and their sync counterparts). These methods allow you to find the element with the maximum or minimum value for a given key selector, rather than just the maximum or minimum value itself. - -For example, to find the blog with the most posts: - -```csharp -var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count()); -``` - -This translates to the following SQL: - -```sql -SELECT TOP(1) [b].[Id], [b].[Name] -FROM [Blogs] AS [b] -ORDER BY ( - SELECT COUNT(*) - FROM [Posts] AS [p] - WHERE [b].[Id] = [p].[BlogId]) DESC -``` - -Similarly, `MinByAsync` orders ascending and returns the element with the minimum value for the key selector. - -### EF.Functions.JsonPathExists() - -EF Core 11 introduces `EF.Functions.JsonPathExists()`, which checks whether a given JSON path exists in a JSON document. On SQL Server, this translates to the [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql) function (available since SQL Server 2022). - -The following query filters blogs to those whose JSON data contains an `OptionalInt` property: - -```csharp -var blogs = await context.Blogs - .Where(b => EF.Functions.JsonPathExists(b.JsonData, "$.OptionalInt")) - .ToListAsync(); -``` - -This generates the following SQL: - -```sql -SELECT [b].[Id], [b].[Name], [b].[JsonData] -FROM [Blogs] AS [b] -WHERE JSON_PATH_EXISTS([b].[JsonData], N'$.OptionalInt') = 1 -``` - -`EF.Functions.JsonPathExists()` accepts a JSON value and a JSON path to check for. It can be used with scalar string properties, complex types, and owned entity types mapped to JSON columns. - -For the full `JSON_PATH_EXISTS` SQL Server documentation, see [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql). - ### Better SQL for to-one joins @@ -190,6 +141,55 @@ ORDER BY [b].[BlogId] Both optimizations can have a significant positive impact on query performance, especially when multiple reference navigations are included. + + +### MaxBy and MinBy + +EF Core now supports translating the LINQ `MaxByAsync` and `MinByAsync` methods (and their sync counterparts). These methods allow you to find the element with the maximum or minimum value for a given key selector, rather than just the maximum or minimum value itself. + +For example, to find the blog with the most posts: + +```csharp +var blogWithMostPosts = await context.Blogs.MaxByAsync(b => b.Posts.Count()); +``` + +This translates to the following SQL: + +```sql +SELECT TOP(1) [b].[Id], [b].[Name] +FROM [Blogs] AS [b] +ORDER BY ( + SELECT COUNT(*) + FROM [Posts] AS [p] + WHERE [b].[Id] = [p].[BlogId]) DESC +``` + +Similarly, `MinByAsync` orders ascending and returns the element with the minimum value for the key selector. + +### EF.Functions.JsonPathExists() + +EF Core 11 introduces `EF.Functions.JsonPathExists()`, which checks whether a given JSON path exists in a JSON document. On SQL Server, this translates to the [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql) function (available since SQL Server 2022). + +The following query filters blogs to those whose JSON data contains an `OptionalInt` property: + +```csharp +var blogs = await context.Blogs + .Where(b => EF.Functions.JsonPathExists(b.JsonData, "$.OptionalInt")) + .ToListAsync(); +``` + +This generates the following SQL: + +```sql +SELECT [b].[Id], [b].[Name], [b].[JsonData] +FROM [Blogs] AS [b] +WHERE JSON_PATH_EXISTS([b].[JsonData], N'$.OptionalInt') = 1 +``` + +`EF.Functions.JsonPathExists()` accepts a JSON value and a JSON path to check for. It can be used with scalar string properties, complex types, and owned entity types mapped to JSON columns. + +For the full `JSON_PATH_EXISTS` SQL Server documentation, see [`JSON_PATH_EXISTS`](/sql/t-sql/functions/json-path-exists-transact-sql). + ## Cosmos DB