【问题标题】:Selecting Grouped Items in EF Core在 EF Core 中选择分组项目
【发布时间】:2021-04-13 10:53:30
【问题描述】:

这在 EF 6.4 中有效:

from a in Addresses
group a by new {a.StreetName, a.StreetNumber} into agrp
where agrp.Count() > 3
from aitem in agrp
select aitem

如果我得到 EF Core 5:

InvalidOperationException:LINQ 表达式“agrp => arp”可能 不被翻译。要么重写查询形式, 翻译,或通过插入显式切换到客户端评估 调用“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或 'ToListAsync'。见https://go.microsoft.com/fwlink/?linkid=2101038 更多信息。

为什么?有没有其他的写法?

【问题讨论】:

    标签: entity-framework entity-framework-core linq-to-entities


    【解决方案1】:

    如果您对将数据加载到内存中没有问题,一个简单的解决方案是在Addresses 之后添加.ToList().AsEnumerable()

    from a in Addresses.ToList() // or .AsEnumerable()
    group a by new {a.StreetName, a.StreetNumber} into agrp
    where agrp.Count() > 3
    from aitem in agrp
    select aitem
    

    请注意,这(在 SqlServer 中)转换为:

    SELECT [a].[Id], [a].[StreetName], [a].[StreetNumber]
    FROM [Addresses] AS [a]
    

    在 EF Core 中,GroupBy(在许多情况下)不翻译为 SQL,而是在内存中运行。 (为避免意外将大量数据加载到内存中,EF 将抛出异常,除非调用 .ToList().AsEnumerable() 以表明这是故意的。)

    (...) 由于没有数据库结构可以表示 IGroupingGroupBy 运算符在大多数情况下没有翻译。当聚合运算符应用于每个返回标量的组时,可以将其转换为关系数据库中的 SQL GROUP BY。 (...)

    -Complex query operators, GroupBy

    该文章还有一个查询示例,该查询转换为group by,并带有Count 上的过滤器(包括在下面)。 不幸的是,该示例并未完全涵盖问题中的示例。它不会返回相关的地址对象,只返回分组键和计数。

    var query = from p in context.Set<Post>()
                group p by p.AuthorId into g
                where g.Count() > 0
                orderby g.Key
                select new
                {
                    g.Key,
                    Count = g.Count()
                };
    
    SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]
    FROM [Posts] AS [p]
    GROUP BY [p].[AuthorId]
    HAVING COUNT(*) > 0
    ORDER BY [p].[AuthorId]
    

    【讨论】:

      【解决方案2】:

      这似乎有效:

      var addressGroupQuery = from a in Addresses
                              group a by new {a.StreetName, a.StreetType} into agrp
                              where agrp.Count() > 3
                              select agrp.Max(a => a.AddressID);
                      
      var addresses = from a in Addresses
                      where addressGroupQuery.Contains(a.AddressID)
                      select a;
      

      注意 addressGroupQuery 仍然是一个延迟查询(没有 ToList)。也会生成一些看起来很干净的 SQL:

      SELECT [a].[AddressID], [a].[City], [a].[Country], [a].[StreetName], [a].[StreetNumber], [a].[StreetType], [a].[UnitNumber]
      FROM [Address] AS [a]
      WHERE EXISTS (
          SELECT 1
          FROM [Address] AS [a0]
          GROUP BY [a0].[StreetName], [a0].[StreetType]
          HAVING (COUNT(*) > 3) AND (MAX([a0].[AddressID]) = [a].[AddressID]))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-05
        • 2020-08-22
        • 1970-01-01
        • 2020-07-21
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多