Skip to content

Translate LINQ CountBy - #39604

Open
officialasishkumar wants to merge 1 commit into
dotnet:mainfrom
officialasishkumar:feature/32741-translate-countby
Open

officialasishkumar wants to merge 1 commit into
dotnet:mainfrom
officialasishkumar:feature/32741-translate-countby

Conversation

@officialasishkumar

Copy link
Copy Markdown
Contributor

Fixes #32741

.NET 9 added Queryable.CountBy, which EF didn't translate, so a query like context.Orders.CountBy(o => o.CustomerId) failed with a translation error.

Change

As suggested in the issue, QueryableMethodNormalizingExpressionVisitor now rewrites

source.CountBy(x => x.Prop)

to

source.GroupBy(x => x.Prop).Select(g => new KeyValuePair<TKey, int>(g.Key, g.Count()))

next to the existing MinBy/MaxBy normalization (#37573). The rest of the pipeline already handles this shape, so all providers that support GroupBy with aggregates get the translation:

SELECT [o].[ProductID] AS [Key], COUNT(*) AS [Value]
FROM [Order Details] AS [o]
GROUP BY [o].[ProductID]
  • The KeyValuePair is created with its Key and Value members (as for an anonymous type), so operators composed after CountBy bind to the grouping key and the count. For example, CountBy(...).OrderByDescending(e => e.Value).Take(5) translates to ORDER BY COUNT(*) DESC with TOP, and CountBy(...).Where(e => e.Value > 40) to HAVING COUNT(*) > 40. Without the members, these member accesses can't be bound and the query fails translation.
  • Enumerable.CountBy over a collection navigation is already converted to Queryable.CountBy by the same visitor, so c.Orders.CountBy(o => o.EmployeeID) in a projection also works (OUTER APPLY on SQL Server).
  • CountBy with a key comparer isn't rewritten and fails translation, as GroupBy with a comparer does.
  • QueryableMethods.CountBy is added (with its API baseline entry), like QueryableMethods.MaxBy/MinBy.

LINQ to Objects' CountBy throws for a null key because it uses a dictionary. The translated query instead returns a row for the null group, as GroupBy does.

Tests

New tests in NorthwindGroupByQueryTestBase: a property key, after a Where, an anonymous type key, Where on the count, OrderByDescending/ThenBy/Take over the result, Select of the key, over a collection navigation in a projection, and with a comparer (translation failure). They come with SQL Server and SQLite baselines. On SQLite the navigation case needs APPLY and asserts that it isn't supported, like the other correlated GroupBy tests there.

Without the change, all of them except the comparer test fail with a translation error. With it, I ran the whole NorthwindGroupByQuery test classes: SQLite (700 passed, 4 skipped), SQL Server against Azure SQL Edge (699 passed, 4 skipped) and InMemory (658 passed, 20 skipped). I only ran these test classes, not the full suite.

  • I've read the guidelines for contributing and seen the walkthrough
  • I've posted a comment on an issue with a detailed description of how I am planning to contribute and got approval from a member of the team
  • The code builds and tests pass locally (also verified by our automated build checks)
  • Commit messages follow this format:
        Summary of the changes
        - Detail 1
        - Detail 2

        Fixes #bugnumber
  • Tests for the changes have been added (for bug fixes / features)
  • Code follows the same patterns and style as existing code in this repo

- Normalize Queryable.CountBy(keySelector) to GroupBy(keySelector).Select(g => new KeyValuePair<TKey, int>(g.Key, g.Count())) in QueryableMethodNormalizingExpressionVisitor, next to the existing MinBy/MaxBy normalization. Enumerable.CountBy over a collection navigation is converted to Queryable.CountBy first, so it is handled as well.
- Create the KeyValuePair with its Key and Value members, so that operators composed over the result bind to the grouping key and the count: Where(e => e.Value > 40) translates to HAVING and OrderByDescending(e => e.Value) to ORDER BY COUNT(*).
- Leave CountBy with a key comparer untranslated, as GroupBy with a comparer is.
- Add QueryableMethods.CountBy and its API baseline entry.
- Add CountBy tests to NorthwindGroupByQueryTestBase with SQL Server and SQLite baselines.

Fixes dotnet#32741
@officialasishkumar
officialasishkumar requested a review from a team as a code owner November 11, 2026 16:07
Copilot AI balanced review requested due to automatic review settings November 11, 2026 16:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Translate LINQ CountBy

2 participants