Linq分组和嵌套分组

Linq grouping and nested grouping

我正在创建一个可以用各种分组显示的 TreeView。基本 类 如下:

public class MachineStatus 
{
  public string MachineName {get;set;} 
  public string Department {get;set;}
  public string LocationName {get;set;}
  public IEnumerable<DeviceStatus> Devices {get;set;}
}

public class DeviceStatus
{
  public string DeviceName {get;set;}
  public string DeviceType {get;set;}
  public string IpAddress {get;set;}
  public ConnectionStatus Status {get;set;}
}

小组将由 DepartmentLocationDeviceType 组成。前两个使用 Linq 很容易。然而,DeviceType 具有挑战性,因为每个 Machine 可能包含多个 Device 个不同类型的对象。

原始数据

Machine 1
  |__ Device A (Type Foo)
  |__ Device B (Type Bar)
Machine 2
  |__ Device C (Type Foo)
  |__ Device D (Type Zed)

结果

Type Bar
  |__ Machine 1
      |__Device B
Type Foo
  |__Machine 1
      |__Device A
  |__Machine 2
      |__Device C
Type Zed
  |__Machine 2
      |__Device D

我想我必须在尝试分组之前展平 MachineStatus 记录的列表,但我在使用 SelectMany 时感到困惑...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsoleApp
{
class Program
{
    static void Main(string[] args) {
        DeviceStatus deviceA = new DeviceStatus() { DeviceName = "A", DeviceType = "foo" };
        DeviceStatus deviceB = new DeviceStatus() { DeviceName = "B", DeviceType = "bar" };
        DeviceStatus deviceC = new DeviceStatus() { DeviceName = "C", DeviceType = "foo" };
        DeviceStatus deviceD = new DeviceStatus() { DeviceName = "D", DeviceType = "zed" };


        MachineStatus status1 = new MachineStatus() { MachineName = "1", Department = "1", LocationName = "1", Devices = new List<DeviceStatus>{ deviceA, deviceB} };
        MachineStatus status2 = new MachineStatus() { MachineName = "2", Department = "2", LocationName = "2", Devices = new List<DeviceStatus> { deviceC, deviceD } };

        List<MachineStatus> machines = new List<MachineStatus>() { status1, status2};

        var result = machines.SelectMany(x => x.Devices, (a, b) => new { DeviceType = b.DeviceType, MachineName = a.MachineName, DeviceName = b.DeviceName } );
        var result2 = result.GroupBy(x => new {x.DeviceType}).Select( z => new { DeviceType = z.Key.DeviceType, Machines = z.ToList().GroupBy(y => y.MachineName) });

        foreach (var group1 in result2)
        {
            foreach (var group2 in group1.Machines)
            {
                foreach (var group3 in group2)
                {
                    Console.Write(group1.DeviceType + ":");
                    Console.Write(group3.MachineName + ":");
                    Console.WriteLine(group3.DeviceName);
                }
            }
        }

        Console.ReadLine();

    }

    public class MachineStatus
    {
        public string MachineName { get; set; }
        public string Department { get; set; }
        public string LocationName { get; set; }
        public IEnumerable<DeviceStatus> Devices { get; set; }
    }

    public class DeviceStatus
    {
        public string DeviceName { get; set; }
        public string DeviceType { get; set; }
        public string IpAddress { get; set; }
        public ConnectionStatus Status { get; set; }
    }

    public class ConnectionStatus
    {
    }
}
}

产生了以下输出:

foo:1:A
foo:2:C
bar:1:B
zed:2:D

分组为:

foo:1:A
foo:2:C

bar:1:B

zed:2:D

您可以像这样使用 SelectMany & GroupBy 实现此目的:-

var result = machines.SelectMany(x => x.Devices, 
                                    (machineObj, devices) => new { machineObj, devices })
                     .GroupBy(x => new { x.devices.DeviceType, x.machineObj.Department, 
                                                             x.machineObj.LocationName })
                     .Select(x => new
                                 {
                                     DeviceType = x.Key.DeviceType,
                                     Department = x.Key.Department,
                                     Location = x.Key.LocationName,
                                     machines = x
                                 });

这是完整的 Working Fiddle,其中包含我使用的示例数据,下面是相同的输出。