首页 > 解决方案 > Linq:获取具有多个嵌套组的内部对象的小计

问题描述

我需要从从 SQL 存储过程中检索到的数据集中创建一些报告。这是数据的表示:

+--------------+-------------------+-----------------+--------------------+
| CustomerName | CustInventoryAcct | ProductCategory | ProductSubCategory |
+--------------+-------------------+-----------------+--------------------+
| ABC          |             12345 | Books           | Fiction            |
| ABC          |             12345 | Books           | Fiction            |
| ABC          |             12345 | Books           | Non-Fiction        |
| ABC          |             67890 | Magazines       | Sports             |
+--------------+-------------------+-----------------+--------------------+

该数据集还包括许多与实际产品相关的数据字段,为简洁起见我省略了这些字段 - 以上是我需要分组的四个标准。

报告需要对这四个字段中的每一个进行分组,因此使用上表,输出将如下所示:

Customer Header: ABC
    Inventory Header: 12345
        Category Header: Books
            Sub Category Header: Fiction
                Detail rows for each of the items in this group (2 using the sample data above)
            Sub Category Footer: Fiction (Item Count : 2)
            Sub Category Header: Non-Fiction
                Detail row
            Sub Category Footer: Non Fiction (Item Count: 1)
        Category Footer: Books (Item Count: 3)
    Inventory Footer: 12345 (Item Count: 3)
    Inventory Header: 67890
        Category Header: Magazines
            Sub Category Header: Sports
                Detail row
            Sub Category Footer: Sports (Item Count: 1)
        Category Footer: Magazines (Item Count: 1)
    Inventory Footer: 67890 (Item Count: 1)
Customer Footer: ABC (Item Count: 4)

我有一个用于分组的 Linq 查询(不确定这是否是最好的方法):

var groupedData = rawData
    .GroupBy(x => new { x.ProductSubCategory, x.ProductCategory, x.CustInventoryAcct, x.Customer })
    .GroupBy(x => new { x.Key.ProductCategory, x.Key.CustInventoryAcct, x.Key.Customer })
    .GroupBy(x => new { x.Key.CustInventoryAcct, x.Key.Customer })
    .GroupBy(x => new { x.Key.Customer })

我使用嵌套foreach循环来构建报告结构:

foreach (var customer in groupedData)
{
    GenerateHeader(customer.Key);
    foreach (var custInventory in customer)
    {
        GenerateHeader(custInventory.Key.CustInventoryAcct);
        foreach(var category in custInventory)
        {
            GenerateHeader(category.Key.ProductCategory);
            foreach(var subCategory in category)
            {
                GenerateHeader(subCategory.Key.ProductSubCategory);
                foreach (var item in subCategory)
                    // Populate detail rows
                // Generate footer
             }
             // Generate footer
        }
        // Generate footer
    }
    // Generate footer    
}
// Generate footer

这部分工作得很好 - 问题是当我试图获取每个页脚的项目数时。我可以使用 获取最后一个嵌套级别(子类别)的项目计数Count(),但如果我在更高级别尝试这样做,它可以预见地给我该级别的组数,而不是层次结构底部的基础项目。我不知道如何向下钻取,以便为每个嵌套级别创建小计。

我可以通过使用一些int计数器来解决这个问题,但我想知道在 Linq 中是否有办法做到这一点。我遇到的困难让我想知道我的分组查询是否不是在 Linq 中处理嵌套组的正确方法。

我已经阅读了一堆关于使用的 SO 答案和文章,GroupBy但仍然无法弄清楚 - 有人可以帮助我了解完成我想要做的事情的最佳方法吗?

标签: c#linqgroup-by

解决方案


尝试以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace ConsoleApplication169
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("CustomerName", typeof(string));
            dt.Columns.Add("CustInventoryAcct", typeof(string));
            dt.Columns.Add("ProductCategory", typeof(string));
            dt.Columns.Add("ProductSubCategory", typeof(string));

            dt.Rows.Add(new object[] { "ABC", "12345", "Books", "Fiction" });
            dt.Rows.Add(new object[] { "ABC", "12345", "Books", "Fiction" });
            dt.Rows.Add(new object[] { "ABC", "12345", "Books", "Non-Fiction" });
            dt.Rows.Add(new object[] { "ABC", "67890", "Magazines", "Sports" });

            foreach(var name in dt.AsEnumerable().GroupBy(x => x.Field<string>("CustomerName")))
            {
                Console.WriteLine("Customer Header: {0}", name.Key);
                foreach (var header in name.GroupBy(x => x.Field<string>("CustInventoryAcct")))
                {
                    Console.WriteLine("    Inventory Header: {0}", header.Key);
                    foreach (var category in name.GroupBy(x => x.Field<string>("ProductCategory")))
                    {
                        Console.WriteLine("        Category Header: {0}", category.Key);
                        foreach (var subCategory in name.GroupBy(x => x.Field<string>("ProductSubCategory")))
                        {
                            Console.WriteLine("            Sub Category Header: {0}", subCategory.Key);
                            foreach (DataRow row in subCategory)
                            {
                            }
                            Console.WriteLine("            Sub Category Footer: {0}", subCategory.Key);
                        }
                        Console.WriteLine("        Category Footer: {0}", category.Key);
                    }
                    Console.WriteLine("    Inventory Footer: {0}", header.Key);
                }
                Console.WriteLine("Customer Footer: {0}", name.Key);
            }
            Console.ReadLine();
        }
    }
  
}

推荐阅读