如何使用多个GroupBy构建LINQ查询?

9

我有一个实体,看起来像这样:

public partial class MemberTank
{
    public int Id { get; set; }
    public int AccountId { get; set; }
    public int Tier { get; set; }
    public string Class { get; set; }
    public string TankName { get; set; }
    public int Battles { get; set; }
    public int Victories { get; set; }
    public System.DateTime LastUpdated { get; set; }
}

一小部分数据示例:

 Id   AccountId   Tier   Class  TankName   Battles  Victories
 ---  ---------   ----   -----  ---------  -------  ----------
 1    432423      5      Heavy  KV         105      58
 2    432423      6      Heavy  IS         70       39
 3    544327      5      Heavy  KV         200      102
 4    325432      7      Medium KV-13      154      110
 5    432423      7      Medium KV-13      191      101

最终,我想要的结果是一个层级列表,其中包含一个类别列表,而在类别中是TankName的不同分组,并且还有Battles和Victories的总和。
是否可以在单个LINQ语句中完成所有这些操作?或者是否有其他简单的方法来获得结果?(我知道我可以轻松地多次循环遍历DbSet以产生所需的列表;我希望使用LINQ更有效地获得相同的结果。)

在 LINQ 中只需使用标准的 Group By 应该就可以了。http://msdn.microsoft.com/en-us/vcsharp/aa336754.aspx#simple1 - Richard Forrest
3个回答

10
这应该可以做到:
var output = from mt in MemberTanks
             group by {mt.Tier, mt.Class, mt.TankName} into g
             select new { g.Key.Tier, 
                          g.Key.Class, 
                          g.Key.TankName, 
                          Fights = g.Sum(mt => mt.Battles), 
                          Wins = g.Sum(mt=> mt.Victories
                        };

哎呀!这个问题其实很简单。我在一开始的尝试中唯一缺失的就是用逗号分隔的 group by。我知道我可以很好地按一个属性进行分组,但对于其他属性,我并没有想象到结果会正是我所需要的。谢谢! - Sailing Judo
实际上,在尝试编译时遇到了各种问题。它根本不喜欢“group by”。 - Sailing Judo
1
@Sailing 请尝试使用 group mt by {mt.Tier, mt.Class, mt.TankName} into g - Magnus
是的,尝试过了。仍然不行。会产生许多“无效表达式项 '{'”错误和“需要 ';'”错误。在那个带有 group by 的行上有 13 个错误。仍在研究中。 - Sailing Judo
1
按照新的 {mt.Tier, mt.Class, mt.TankName} 分组为 g 的数据集合 mt Wins = g.Sum(mt => mt.Victories) - Magnus
@Magnus,谢谢。问题就在于“new”这个关键字。我已经发现了括号漏掉的问题。 - Sailing Judo

6
你也可以使用方法语法。这应该会给你与@TheEvilGreebo相同的结果。
var result = memberTanks.GroupBy(x => new {x.Tier, x.Class, x.TankName})
                        .Select(g => new { g.Key.Tier, 
                                           g.Key.Class, 
                                           g.Key.TankName, 
                                           Fights = g.Sum(mt => mt.Battles), 
                                           Wins = g.Sum(mt=> mt.Victories)
                        });

使用哪种语法取决于个人偏好。 去掉 .Select 以返回 IGrouping,这将使您能够枚举组。

var result = memberTanks.GroupBy(x => new {x.Tier, x.Class, x.TankName})

1

我一直在尝试从The Evil Greebo的答案中获得有用的结果。虽然这个答案确实会产生结果(在修复响应中提到的编译问题后),但它并没有给我我真正想要的东西(这意味着我在问题中没有解释清楚自己的意思)。

Feanz在我的问题中留下了一条评论,让我查看带有LINQ示例的MS网站,尽管我以前认为我已经在那里查找过了,但这次我找到了他们嵌套分组的示例,并按照他们的方式尝试了一下。以下代码正好给我我想要的东西:

var result = from mt in db.MemberTanks
             group mt by mt.Tier into tg
             select new
             {
                 Tier = tg.Key,
                 Classes = from mt in tg
                           group mt by mt.Class into cg
                           select new
                           {
                               Class = cg.Key,
                               TankTypes = from mt in cg
                                           group mt by mt.TankName into tng
                                           select new
                                           {
                                               TankName = tng.Key,
                                               Battles = tng.Sum(mt => mt.Battles),
                                               Victories = tng.Sum(mt => mt.Victories),
                                               Count = tng.Count()
                                           }
                           }
             };

我会让Greebo先生的答案保持原样,因为大多数人可能从中获得最佳结果。


1
之前的答案最终产生了一个单一的非嵌套结果集。智能感应暗示结果将是多个嵌套组,但当我运行代码时,结果集是一个单一列表。我的答案中的代码会生成非常明显的分组,每个分组都在自己的结果集中嵌套,就像我预期的那样。 - Sailing Judo
我删除了一些回答,因为我改变了将自己的回复标记为正确答案的想法。Greebo的回答获得了最多的赞同,虽然它并不完全符合我的需求,但仍然是正确的,并且很可能更适合未来的回答者。 - Sailing Judo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接