【问题标题】:How to flatten nested objects (LINQ)如何展平嵌套对象 (LINQ)
【发布时间】:2017-01-21 07:38:22
【问题描述】:

我在一个旧的 Winforms 网格上做一些工作,我有两个模型,我试图将它们展平并分配给一个 DataGridView。

这是我的示例模型。

public class StockItem
{
     public string StockName { get; set; }
     public int Id { get; set; }
     public List<Warehouse> Warehouses { get; set; }
}

public class Warehouse
{
     public string WarehouseName { get; set; }
     public int Id { get; set; }
}

数据的工作方式必须首先创建一个仓库,然后分配给每个StockItemStockItem 可能拥有所有仓库,也可能只有一个。

我需要展平数据,以便网格显示StockName,然后显示库存项目的所有关联仓库。

例子

StockCode1      Warehouse1   Warehouse2   Warehouse3
StockCode2      Warehouse1   Warehouse2   
StockCode2      Warehouse1                Warehouse3   

我尝试通过 Linq 查询来执行此操作,但只能获取每个 StockItem\Warehouse 的记录。

【问题讨论】:

  • 也许这会有所帮助? stackoverflow.com/questions/6428940/…
  • 这并不是真正的扁平化,而是交叉表/旋转
  • 生成的数据类型是什么?与StockItem 关联的可能仓库数量是否有上限?我对 WinForms DataGridView 不熟悉,所以我可能缺少一些东西,但您不必将它绑定到强类型对象的集合吗?如果仓库数量不定,您似乎无法做到这一点。
  • 你能创建一段代码并分享你是如何构建数据的吗?但一般来说,您应该能够使用 LINQ 查询获取数据。

标签: c# linq datagridview models


【解决方案1】:

您可以通过创建一个DataTable 来实现它,您可以轻松地将其用作gridview 的源。首先添加所有列,然后为每个库存添加仓库:

var warehouseNames = 
    stocks
    .SelectMany(x => x.Warehouses.Select(y => y.WarehouseName)).Distinct();

var dt = new DataTable();
dt.Columns.Add("StockCode");

foreach (var name in warehouseNames)
{
    dt.Columns.Add(name);
}

foreach (var stock in stocks)
{
    var row = dt.NewRow();
    row["StockCode"] = stock.Id;
    foreach (var warehouse in stock.Warehouses)
    {
        row[warehouse.WarehouseName] = warehouse.Id;
    }
    dt.Rows.Add(row);
}

【讨论】:

    【解决方案2】:

    类似这样的:

    var availableWarehouses = new [] { 
        new Warehouse { 
            WarehouseName = "Warehouse1",
            Id = 1
        },
        new Warehouse {
            WarehouseName = "Warehouse2",
            Id = 2
        },
        new Warehouse {
            WarehouseName = "Warehouse3",
            Id = 3
        }
    };
    
    var stocks = new [] {
        new StockItem {
            StockName = "StockCode1",
            Id = 1,
            Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1], availableWarehouses[2] }
        },
        new StockItem {
            StockName = "StockCode2",
            Id = 2,
            Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1] }
        },
        new StockItem {
            StockName = "StockCode3",
            Id = 3,
            Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[2] }
        }
    };
    
    var flatten = stocks.Select(item => new {
            StockName = item.StockName,
            WarehousesNames = availableWarehouses.Select(warehouse => item.Warehouses.Contains(warehouse) ? warehouse.WarehouseName : "          ")
                .Aggregate((current, next) => current + "\t" + next)
        });
    
    foreach(var item in flatten) {
        Console.WriteLine("{0}\t{1}", item.StockName, item.WarehousesNames);
    }
    

    【讨论】:

      【解决方案3】:

      这应该可以满足您的需求:

      var flattened = stockItems
          .Select(x => new { 
                       StockName = x.StockName, 
                       WarehouseNames = x.Warehouses
                                        .Select(y => y.WarehouseName)
                                        .ToList() })
          .ToList();
      

      这将导致包含StockName 的项目集合和WarehouseName 字符串列表。已添加ToList 以枚举查询。

      对于这些示例数据:

      List<StockItem> stockItems = new List<StockItem>
      {
          new StockItem
          {
              StockName ="A",
              Id = 1,
              Warehouses = new List<Warehouse>
              {
                  new Warehouse { Id = 1, WarehouseName = "x" },
                  new Warehouse { Id = 2, WarehouseName = "y" }
              }
          },
          new StockItem
          {
              StockName = "B",
              Id = 2,
              Warehouses = new List<Warehouse>
              {
                  new Warehouse { Id = 3, WarehouseName = "z" },
                  new Warehouse { Id = 4, WarehouseName = "w" }
              }
          }
      };
      

      我得到了以下结果:

      【讨论】:

        【解决方案4】:

        我确实推荐它,但是您可以使用dynamic 对象来创建具有您想要的形状的对象。 这样做不是常见的 C# 模式。这在 Python 或 Javascript 等语言中更为常见。

        C# 是一种强类型语言,在绝对必要时才应考虑进入动态对象的世界(想想解析 json blob)。我强烈认为您重新评估了您需要做的事情并从不同的角度进行处理。

        【讨论】:

          猜你喜欢
          • 2011-09-19
          • 2018-10-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-02
          • 2020-12-22
          • 2020-11-17
          相关资源
          最近更新 更多