У меня есть объект, который выглядит так:

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 с суммами сражений и побед.

Можно ли сделать все это в одном операторе LINQ? Или есть другой способ легко получить результат? (Я знаю, что могу легко перебрать DbSet несколько раз, чтобы создать список, который мне нужен; я надеюсь на более эффективный способ получения того же результата с LINQ.)

9
Sailing Judo 12 Июн 2011 в 22:51
Для этого достаточно использовать стандартную группу в linq. msdn.microsoft.com/en-us/vcsharp/aa336754.aspx# simple1
 – 
Richard Forrest
12 Июн 2011 в 23:03

3 ответа

Лучший ответ

Это должно сделать это:

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
                        };
10
The Evil Greebo 12 Июн 2011 в 23:50
Дох! Это было так просто. Группа с запятыми была единственным, чего мне действительно не хватало в моих первоначальных попытках. Я знал, что могу сгруппировать по одному объекту, но не по другим, и не предполагал, что результат будет именно тем, что мне нужно. Спасибо
 – 
Sailing Judo
13 Июн 2011 в 01:31
На самом деле, возникают всевозможные проблемы при попытке его скомпилировать. Совершенно не нравится "группировать по".
 – 
Sailing Judo
13 Июн 2011 в 01:47
1
Попробуйте group mt by {mt.Tier, mt.Class, mt.TankName} into g
 – 
Magnus
13 Июн 2011 в 02:02
Да, пробовал. По-прежнему никуда. Выдает множество ошибок типа «Недопустимый термин выражения '{'» и «; ожидаемых» ошибок. Всего 13 ошибок в строке с group by. Все еще изучаю.
 – 
Sailing Judo
13 Июн 2011 в 02:22
1
group mt by new {mt.Tier, mt.Class, mt.TankName} into g и Wins = g.Sum(mt=> mt.Victories)
 – 
Magnus
13 Июн 2011 в 02:29

Вы также можете использовать синтаксис метода. Это должно дать вам то же самое, что и @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})
6
NinjaNye 16 Июл 2013 в 14:39

Я продолжал пытаться получить полезные результаты из ответа Злого Грибо. Хотя ответ действительно дает результаты (после исправления проблем с компиляцией, упомянутых в ответах), он не дает мне того, что я действительно искал (что означает, что я недостаточно хорошо объяснил себя в вопросе).

Feanz оставил комментарий в моем вопросе, чтобы проверить сайт MS с примерами LINQ, и, хотя я думал, что заглядывал туда раньше, на этот раз я нашел их пример вложенных групповых байтов и попробовал их. Следующий код дает мне именно то, что я искал:

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()
                                           }
                           }
             };

Я оставлю ответ г-на Грибо проверенным, так как большинство людей, вероятно, получит от этого наилучшие результаты.

1
Sailing Judo 13 Июн 2011 в 21:01
1
Что ж, результаты предыдущих ответов в конечном итоге дали единый невложенный набор результатов. Intellisense подразумевает, что результатом будет несколько вложенных групп, но когда я запустил код, набор результатов был одним списком. Код в моем ответе создает очень разные группы, каждая из которых имеет свой собственный набор результатов, вложенных туда, где я ожидал.
 – 
Sailing Judo
13 Июн 2011 в 20:38
Я удалил часть своего ответа, так как передумал отмечать свой ответ как правильный. Ответ Greebo получил наибольшее количество голосов, и хотя он не совсем соответствовал тому, что мне было нужно, он все еще верен и, скорее всего, лучше подойдет будущим ищущим ответы.
 – 
Sailing Judo
13 Июн 2011 в 21:03