Skip to content

Translate GroupBy followed by FirstOrDefault (and similar) using ROW_NUMBER() #36380

Description

@chenghehe

What problem are you trying to solve?

var result = await dbContext.YourTable
.GroupBy(e => e.GroupColumn)
.Select(g => g.OrderBy(e => e.SortColumn).FirstOrDefault())
.ToListAsync();

This way of writing will prompt an exception and cannot run

Describe the solution you'd like

No response

Activity

  1. changed the title [-]EF core 中如何分组后取出第一条数据?[/-] [+]How to extract the first data after grouping in EF core?[/+] on Jul 17, 2025
  2. chenghehe commented on Jul 17, 2025

    @chenghehe
    Author

    abnormal information:The given key 'EmptyProjectionMember' was not present in the dictionary

  3. changed the title [-]How to extract the first data after grouping in EF core?[/-] [+]Translate GroupBy followed by FirstOrDefault (and similar) using window function[/+] on Jul 17, 2025
  4. roji commented on Jul 17, 2025

    @roji
    Member

    @chenghehe the LINQ query as your wrote it is currently not translatable to SQL, since SQL GROUP BY requires an aggregate function (MAX, SUM...) to reduce the group back to a table; see this doc page for more information on this. Such queries typically require the use of SQL window functions, but these aren't yet supported by EF (see #12747).

    In the meantime, if you're OK with bringing the rows back from the database and performing the OrderBy and FirstOrDefault on the client, add AsEnumerable() for client evaluation after the GroupBy (but for large numbers of rows this could be problematic for performance).

    Am putting this issue in the backlog to track translating the above query pattern via window functions:

    var result = await dbContext.YourTable
        .GroupBy(e => e.GroupColumn)
        .Select(g => g.OrderBy(e => e.SortColumn).FirstOrDefault())
        .ToListAsync();
    SELECT *
    FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY [GroupColumn] ORDER BY [SortColumn]) AS rn
        FROM [YourTable]
    ) ranked
    WHERE rn = 1;

    The same can be done for ElementAt, Last, etc.

  5. added theissue type on Aug 6, 2025
  6. added this to the Backlog milestone on Aug 6, 2025
  7. self-assigned this
    on Aug 6, 2025
  8. changed the title [-]Translate GroupBy followed by FirstOrDefault (and similar) using window function[/-] [+]Translate GroupBy followed by FirstOrDefault (and similar) using ROW_NUMBER()[/+] on Aug 6, 2025
  9. m-gasser commented on Aug 6, 2025

    @m-gasser
  10. bachratyg commented on Aug 6, 2025

    @bachratyg
  11. roji commented on Aug 6, 2025

    @roji
  12. m-gasser commented on Aug 7, 2025

    @m-gasser
  13. roji commented on Aug 7, 2025

    @roji
  14. QHarshil commented on Oct 5, 2026

    @QHarshil
    Contributor

    I've opened #39173 for this. It translates the query to the ROW_NUMBER() filter alone, without the GROUP BY and the join back.

  15. added a commit that references this issue on Oct 14, 2026
    e7dbdb7
  16. added a commit that references this issue on Oct 14, 2026
    757e2d0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions