【问题标题】:Error while flattening the IQueryable<T> after GroupBy()在 GroupBy() 之后展平 IQueryable<T> 时出错
【发布时间】:2021-03-08 08:45:31
【问题描述】:

我正在尝试从数据库中获取展平对象,但出现错误。我想在数据库中获取所有类型的 2 本书,代码如下所示:

IQueryable<Book> query = _context.Books
                                 .GroupBy(b => b.Genre)
                                 .SelectMany(bc => bc.Select(b => b).Take(2));

有人知道我在这里做错了什么吗?

我得到了这个异常而不是结果:

无法翻译 LINQ 表达式“bc =&gt; bc .AsQueryable() .Select(b =&gt; b) .Take(2)”。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。有关详细信息,请参阅 go.microsoft.com/fwlink/?linkid=2101038。

我也尝试过类似的方法:

IQueryable<Book> query = _context.Genres.GroupJoin(
     _context.Books,
     g => g,
     b => b.Genre,
     (g, books) => new
     {
         Genre = g,
         BookCollection = books
     }
    ).SelectMany(bc => bc.BookCollection.Select(b => b)
    .Take(2)).Include(b => b.Author).Include(b => b.Rating)
             .Include(b => b.BookISBNs).Include(b => b.Reviews)
             .ThenInclude(r => r.User);

【问题讨论】:

  • 如果您告诉我们您认为自己做错了什么(或为什么您认为自己做错了什么),这可能会有所帮助。
  • 您希望查询返回什么?
  • 是的,当然,它给了我异常而不是结果:LINQ 表达式 'bc => bc .AsQueryable() .Select(b => b) .Take(2)' 不可能翻译。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用显式切换到客户端评估。请参阅go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。
  • 如果它给您异常,请始终在帖子中包含错误消息。
  • @Marta "我希望它返回 Book 对象的 IQueyrable。" - _context.Books.AsQueryable() 也会给你一个 IQueryable&lt;Book&gt;。但你并不是真的想要那样。请明确说明(在帖子中)您期望的结果。

标签: c# entity-framework linq


【解决方案1】:

此尝试远离 group by,这在数据库和 EF 查询转换中受到限制。相反,我们将使用子查询。

var genres = _context.Books.Select(b => b.Genre).Distinct();
var books =
  from genre in genres
  from book in _context.Books.Where(b => b.Genre == genre).Take(2)
  select book;

var results = books.ToArrayAsync();

针对这种翻译:

SELECT c.*
FROM
  (SELECT DISTINCT Genre FROM Books) as a,
  (SELECT top 2 b.* FROM Books b WHERE b.Genre = a.Genre) as c

【讨论】:

【解决方案2】:

因此,您希望按GenreBook 实体进行分组,然后获取一个包含每组两本书的平面列表。我认为您无法通过 LINQ 查询来表达这一点,而 GroupBy() Entity Framework 可以将其转换为 SQL。

但这不是EF的错或缺点。 SQL 中的GROUP BY 仅限于返回分组键和/或对组执行的任何聚合。例如,您可以按Genre 对书籍进行分组,然后想要每个组的类型和书籍总数 -

var result = dbCtx.Books
    .GroupBy(p => p.Genre)
    .Select(g => new
    {
        Genre = g.Key,       // grouping key
        Count = g.Count()    // aggregation on group
    })
    .ToList();

EF 可以为此生成 SQL -

SELECT [b].[Genre] AS [Key], COUNT(*) AS [Count]
FROM [Books] AS [b]
GROUP BY [b].[Genre]

此参考可能会有所帮助 - GroupBy

由于没有数据库结构可以表示 IGrouping,GroupBy 操作员在大多数情况下没有翻译。当一个聚合 运算符应用于每个组,它返回一个标量,它可以是 在关系数据库中翻译为 SQL GROUP BY。 SQL 分组依据 也是有限制的。它要求您仅按标量值进行分组。 投影只能包含分组键列或任何聚合 应用于列。

因此,对于您的查询,您必须获取书籍并在客户端进行分组,例如 -

var result = dbCtx.Books.AsEnumerable()
    .GroupBy(p => p.Genre)
    .SelectMany(g => g.Select(book => book).Take(2).ToList())
    .ToList();

【讨论】:

  • 但我想在 Task 方法中将结果作为 return await result.ToArrayAsync() 返回,所以它不像那样工作,第二个问题是我需要加入它从包含书籍 ID 为 FK 的其他表中记录,例如 BookIsbn、revievs 等。我知道我可以通过包含来做到这一点,但仅限于一种特定类型。
  • @Marta 我已经更新了平面列表的答案和查询。因此,请尝试更新后的查询。只需将 ToList() 替换为 ToArrayAsync() 即可获得带有异步调用的 Book[]。如果您有任何其他问题,请考虑为他们发布新问题。
猜你喜欢
  • 2016-01-05
  • 2022-12-28
  • 1970-01-01
  • 2020-12-22
  • 2013-09-03
  • 2020-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-09-12
相关资源
最近更新 更多