【问题标题】:Entity Framework select one of each group by date实体框架按日期选择每个组之一
【发布时间】:2013-04-22 19:33:01
【问题描述】:

我有一张这样的表(表名:帖子):

+----+--------------------------+-------+------------+
| id |         content          | type  |    date    |
+----+--------------------------+-------+------------+
|  0 | Some text                | TypeA | 2013-04-01 |
|  1 | Some older text          | TypeA | 2012-03-01 |
|  2 | Some even older texttext | TypeA | 2011-01-01 |
|  3 | A dog                    | TypeB | 2013-04-01 |
|  4 | And older dog            | TypeB | 2012-03-01 |
|  5 | An even older dog        | TypeB | 2011-01-01 |
+----+--------------------------+-------+------------+

使用 LINQ 表达式我想找到每种类型的最新内容,所以结果应该是

Some text | TypeA 
A dog     | TypeB

我尝试了一些方法,但没有必要指出无效的表达式。

【问题讨论】:

    标签: c# linq entity-framework


    【解决方案1】:

    根据记忆,应该这样做

    var data = context.Posts.GroupBy(p => p.Type)
                            .Select(g => new { 
                                              Type = g.Key, 
                                              Date = g.OrderByDescending(p => p.Date)
                                                      .FirstOrDefault()
                                             }
                   
    

    这会给你一个新的匿名类型,但你总是可以将它映射到一个类

    【讨论】:

    • 你的意思是 GroupBy(...)
    【解决方案2】:

    如果您想获得整个帖子。你可以试试这个:

    var query = Posts.GroupBy(p => p.Type)
                      .Select(g => g.OrderByDescending(p => p.Date)
                                    .FirstOrDefault()
                       )
    

    【讨论】:

    • group by 是在运行查询之前还是之后运行?那么它会从数据库中提取每一行然后对它们进行分组,还是会在查询中添加一个 group by 语句以便只提取分组的行?
    • @Samir:这确实是一个很好的观点。根据 EF 上的版本,它似乎有不同的行为。 EF 7 似乎将数据加载到客户端并在那里进行分组:stackoverflow.com/questions/11564311/…。 EF 核心似乎支持 sql group by:github.com/aspnet/EntityFrameworkCore/pull/9872。要确认这一点,您可以使用分析器查看实际的 sql 查询。
    • @Samir:如果您使用的是EF Core最新版本并且性能可以接受,即使sql查询不使用group by,您也可以考虑这个解决方案(将代码包装在一个函数中,这样我们如果需要,以后可能会重构),似乎人们已经考虑到了这一点,以后的版本可能会支持这一点。否则,您必须编写原始 sql 查询。
    • 只是为了确认,EF Core 2.1 不会在服务器端进行分组。问题是 GroupBy 之后的所有运算符都在客户端上运行,包括 .Take(n) 运算符。所以 GroupBy(..).Take(n) 会加载所有记录。
    【解决方案3】:

    我想您可以按类型对您的帖子行进行分组,然后从按日期降序排列的该类型集合中选择第一个内容

    from row in Posts 
    group row by row.type 
    into g
    select new
    {       
        Content  = (from row2 in g orderby row2.date descending select row2.content).FirstOrDefault(),
        Type = g.Key
    }
    

    【讨论】:

    • 这似乎有效(在添加“,”并首先更改为 FirstOrDefault() 之后。有没有办法获取整个帖子,或者我需要在“选择新“?
    • @Todilo 我无法在没有模型的情况下对其进行测试,非常抱歉出现错误:) 您可以通过将返回结果从 row2.content 更改为 row2 来获取帖子,但无论如何 group by 都是必需的,因为您需要“EACH TYPE”的最新内容。您也可以尝试获取 Distinct 类型,然后 foreach 类型选择按日期排序的前 1 个帖子,我想这个查询会更有效
    • 我明白了。我将再次“手动”创建模型,这似乎是最有效的方法。非常感谢!
    【解决方案4】:

    或者使用临时结果和谓词

    var tmp = posts.GroupBy(x => x.type).Select(x => new {x.Key, date = x.Max(g => g.date)).ToArray();
    
    var filter = PredicateBuilder.False<Post>();
    
    foreach (var item in tmp)
    {
        filter = filter.Or(x => x.type == item.Key && x.date == item.date);
    }
    
    var newestPosts = posts.Where(filter);
    

    【讨论】:

    • 因为上面的例子确实有效,我还没有测试过。以前没见过 PredicateBuilder 但似乎是我应该学习的一个很酷的功能。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多