13

我需要在具有实体框架的表中为每个组获取前 10 行。基于 SO 上的其他解决方案,我尝试了两件事:

var sendDocuments = await context.Set<DbDocument>
    .Where(t => partnerIds.Contains(t.SenderId))
    .GroupBy(t => t.SenderId)
    .Select(t => new
    {
        t.Key,
        Documents = t.OrderByDescending(t2 => t2.InsertedDateTime).Take(10)
    })                
    .ToArrayAsync();

错误:

System.InvalidOperationException: 'The LINQ expression
'(GroupByShaperExpression: KeySelector: (d.SenderId), 
ElementSelector:(EntityShaperExpression: 
    EntityType: DbDocument
    ValueBufferExpression: 
        (ProjectionBindingExpression: EmptyProjectionMember)
    IsNullable: False ) )
    .OrderByDescending(t2 => t2.InsertedDateTime)' could not be translated. Either rewrite the query in a form that can be translated,
> or switch to client evaluation explicitly by inserting a call to
> either AsEnumerable(), AsAsyncEnumerable(), ToList(), or
> ToListAsync().

var sendDocuments2 = await context.Set<DbDocument>
    .Where(t => partnerIds.Contains(t.SenderId))
    .GroupBy(t => t.SenderId)
    .SelectMany(t => t.OrderByDescending(t2 => t2.InsertedDateTime).Take(10))
    .ToArrayAsync();

错误:

System.InvalidOperationException: 'NavigationExpandingExpressionVisitor' 处理 LINQ 表达式 't => t .OrderByDescending(t2 => t2.InsertedDateTime) .AsQueryable() .Take(10)' 失败。这可能表示 EF Core 中的错误或限制。

还有什么想法吗?

4

1 回答 1

20

更新(EF Core 6.0):

EF Core 6.0 增加了对翻译GroupBy结果集投影的支持,因此获取 (key, items) 的原始代码现在可以正常工作,即

var query = context.Set<DbDocument>()
    .Where(e => partnerIds.Contains(e.SenderId))
    .GroupBy(e => e.SenderId)
    .Select(g => new
    {
        g.Key,
        Documents = g.OrderByDescending(e => e.InsertedDateTime).Take(10)
    });

但是展平(通过SelectMany)仍然不受支持,因此如果您需要这样的查询形状,则必须使用以下解决方法。

原版(EF Core 3.0/3.1/5.0):

这是一个常见问题,遗憾的是 EF Core 3.0/3.1/5.0 查询翻译器不支持专门用于GroupBy.

解决方法是通过关联 2 个子查询(一个用于键,一个用于对应数据)来手动进行探索。

将其应用于您的示例将是这样的。

如果您需要 (key, items) 对:

var query = context.Set<DbDocument>()
    .Where(t => partnerIds.Contains(t.SenderId))
    .Select(t => t.SenderId).Distinct() // <--
    .Select(key => new
    {
        Key = key,
        Documents = 
            context.Set<DbDocument>().Where(t => t.SenderId == key) // <--
                 .OrderByDescending(t => t.InsertedDateTime).Take(10)
                 .ToList() // <--
    });

如果您只需要包含每个键的前 N ​​个项目的平面结果集:

var query = context.Set<DbDocument>()
    .Where(t => partnerIds.Contains(t.SenderId))
    .Select(t => t.SenderId).Distinct() // <--
    .SelectMany(key => context.Set<DbDocument>().Where(t => t.SenderId == key) // <--
        .OrderByDescending(t => t.InsertedDateTime).Take(10)
    );
于 2019-12-24T11:53:03.910 回答