跳至页脚内容
使用 IRONXL

如何使用 C# Interop 与 IronXL.Excel 在 Excel 中创建透视表

在 Excel 中以编程方式生成数据透视表需要使用 C# Interop 及其 Office 依赖项,或者使用像IronXL这样可以独立工作的现代库。 本教程演示了这两种方法,强调了为什么开发人员越来越倾向于选择IronXL而不是传统的Interop方法。

本文将介绍如何编辑、创建、设计和计算数据透视表和数据透视组,以及如何进行自动分析和错误处理。

什么是Excel数据透视表?

数据透视表是Excel中最强大的工具之一。 它是一种简单易用的方法来总结大型数据集。 因此,数据透视表允许您轻松显示、理解和分析数值数据。 数据透视表不仅在 Excel 中可用,而且在其他程序中也可用,例如 Google Sheets、Apple Numbers 和 CSV 导出。 它提供了一种查看数据概览的解决方案——充当数据控制台,让人们能够以有意义的方式查看他们的信息——并与现实联系起来。

让我们先用错误的方法创建一个数据透视表,然后再学习如何用 C# 正确地创建它:

如何使用 C# Interop 在 Excel 表格中创建数据透视表?

C# Excel Interop 通过 COM 自动化提供对 Excel 数据透视表功能的直接访问。 这是许多开发人员在寻找工具以在C#中生成数据透视表时发现的传统方法:(不推荐)

如何在 .NET 中以传统方式创建数据透视表

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

// Create Excel application instance
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"C:\Data\SalesData.xlsx");
Excel.Worksheet xlSheet = (Excel.Worksheet)xlWorkbook.Sheets[1];
Excel.Worksheet xlPivotSheet = (Excel.Worksheet)xlWorkbook.Sheets.Add();

// Define data range for pivot table
Excel.Range dataRange = xlSheet.UsedRange;

// Create pivot cache and pivot table
Excel.PivotCache pivotCache = xlWorkbook.PivotCaches().Create(
    Excel.XlPivotTableSourceType.xlDatabase,
    dataRange,
    Type.Missing);
Excel.PivotTable pivotTable = pivotCache.CreatePivotTable(
    xlPivotSheet.Cells[3, 1],
    "SalesPivot",
    Type.Missing,
    Type.Missing);

// Configure pivot table fields
Excel.PivotField productField = (Excel.PivotField)pivotTable.PivotFields("Product");
productField.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
productField.Position = 1;
Excel.PivotField regionField = (Excel.PivotField)pivotTable.PivotFields("Region");
regionField.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
regionField.Position = 1;
Excel.PivotField salesField = (Excel.PivotField)pivotTable.PivotFields("Sales");
pivotTable.AddDataField(salesField, "Sum of Sales", Excel.XlConsolidationFunction.xlSum);

// Save and cleanup
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();

// Release COM objects to prevent memory leaks
Marshal.ReleaseComObject(pivotTable);
Marshal.ReleaseComObject(pivotCache);
Marshal.ReleaseComObject(xlPivotSheet);
Marshal.ReleaseComObject(xlSheet);
Marshal.ReleaseComObject(xlWorkbook);
Marshal.ReleaseComObject(xlApp);
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

// Create Excel application instance
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"C:\Data\SalesData.xlsx");
Excel.Worksheet xlSheet = (Excel.Worksheet)xlWorkbook.Sheets[1];
Excel.Worksheet xlPivotSheet = (Excel.Worksheet)xlWorkbook.Sheets.Add();

// Define data range for pivot table
Excel.Range dataRange = xlSheet.UsedRange;

// Create pivot cache and pivot table
Excel.PivotCache pivotCache = xlWorkbook.PivotCaches().Create(
    Excel.XlPivotTableSourceType.xlDatabase,
    dataRange,
    Type.Missing);
Excel.PivotTable pivotTable = pivotCache.CreatePivotTable(
    xlPivotSheet.Cells[3, 1],
    "SalesPivot",
    Type.Missing,
    Type.Missing);

// Configure pivot table fields
Excel.PivotField productField = (Excel.PivotField)pivotTable.PivotFields("Product");
productField.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
productField.Position = 1;
Excel.PivotField regionField = (Excel.PivotField)pivotTable.PivotFields("Region");
regionField.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
regionField.Position = 1;
Excel.PivotField salesField = (Excel.PivotField)pivotTable.PivotFields("Sales");
pivotTable.AddDataField(salesField, "Sum of Sales", Excel.XlConsolidationFunction.xlSum);

// Save and cleanup
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();

// Release COM objects to prevent memory leaks
Marshal.ReleaseComObject(pivotTable);
Marshal.ReleaseComObject(pivotCache);
Marshal.ReleaseComObject(xlPivotSheet);
Marshal.ReleaseComObject(xlSheet);
Marshal.ReleaseComObject(xlWorkbook);
Marshal.ReleaseComObject(xlApp);
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

此 Interop 示例创建了一个原生 Excel 数据透视表,其中产品为行,地区为列,销售额汇总在数据区域中。 虽然功能正常,但这种方法需要安装Microsoft Office并仔细管理COM对象。 请参阅微软文档,了解为什么不应在现代开发中使用此方法。

C# 互操作会带来哪些问题?

Interop方法面临几个重大挑战:

遗憾的是,Stack Overflow 和其他编程网站仍然推荐它,因为它们的讨论主题仍然停留在 2000 年代初期的想法上。

部署依赖项:需要在运行源代码的每台机器上安装 Microsoft Office,包括生产服务器。 这增加了许可成本和部署复杂性。

内存管理:必须使用Marshal.ReleaseComObject()显式释放 COM 对象。 即使遗漏一个对象也会导致Excel进程挂在内存中,如Stack Overflow上详细记录

平台限制详情:这种老式的解决方案仅适用于安装了 Office 的 Windows 系统,速度可能非常慢,令用户感到困惑,并且可能导致内存泄漏。 不支持Linux、macOS、Docker容器或像Azure Functions这样的云平台。

性能问题:启动Excel应用程序实例速度慢且资源密集,尤其是用于服务器端处理时。

版本兼容性:不同的Office版本可能具有不同的COM接口,导致跨环境的兼容性问题。

IronXL 如何在不使用互操作的情况下以编程方式创建数据透视表?

IronXL使用托管代码创建数据透视表,没有 COM 依赖项,其创建方式有所不同。 虽然它不能创建 Excel 原生数据透视表,但它提供了强大的聚合功能。

如何以现代方式通过编程创建 XLSX 或 XLS 数据透视表

using IronXL;
using System.Linq;
using System.Data; // Keep this namespace
using static System.Data.DataTableExtensions; // Use 'using static' for DataTableExtensions

class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - no Office required
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];
        // Convert to DataTable for powerful manipulation
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers
        // Create pivot-style aggregation using LINQ
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) //range
            .Select(g => new {
                Product = g.Key.Product,
                Region = g.Key.Region,
                TotalSales = g.Sum(row => Convert.ToDecimal(row["Sales"])),
                AverageSale = g.Average(row => Convert.ToDecimal(row["Sales"])),
                Count = g.Count()
            });
        // Create pivot report worksheet
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");
        // Build cross-tabulation structure
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);
        // Create headers
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // string
            col++;
        }
        // Populate pivot data
        int row = 2;
        foreach (var product in products)
        {
            pivotSheet[$"A{row}"].Value = product;
            col = 2;
            foreach (var region in regions)
            {
                var sales = pivotData
                    .Where(p => p.Product == product && p.Region == region)
                    .Select(p => p.TotalSales)
                    .FirstOrDefault();
                pivotSheet[$"{(char)('A' + col - 1)}{row}"].Value = sales;
                col++;
            }
            row++;
        }
        // Add totals using Excel formulas
        pivotSheet[$"A{row}"].Value = "Total"; // grand totals
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }
        // Proceeding to apply formatting
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";
        workbook.SaveAs("PivotReport.xlsx");
    }
}
using IronXL;
using System.Linq;
using System.Data; // Keep this namespace
using static System.Data.DataTableExtensions; // Use 'using static' for DataTableExtensions

class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - no Office required
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];
        // Convert to DataTable for powerful manipulation
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers
        // Create pivot-style aggregation using LINQ
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) //range
            .Select(g => new {
                Product = g.Key.Product,
                Region = g.Key.Region,
                TotalSales = g.Sum(row => Convert.ToDecimal(row["Sales"])),
                AverageSale = g.Average(row => Convert.ToDecimal(row["Sales"])),
                Count = g.Count()
            });
        // Create pivot report worksheet
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");
        // Build cross-tabulation structure
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);
        // Create headers
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // string
            col++;
        }
        // Populate pivot data
        int row = 2;
        foreach (var product in products)
        {
            pivotSheet[$"A{row}"].Value = product;
            col = 2;
            foreach (var region in regions)
            {
                var sales = pivotData
                    .Where(p => p.Product == product && p.Region == region)
                    .Select(p => p.TotalSales)
                    .FirstOrDefault();
                pivotSheet[$"{(char)('A' + col - 1)}{row}"].Value = sales;
                col++;
            }
            row++;
        }
        // Add totals using Excel formulas
        pivotSheet[$"A{row}"].Value = "Total"; // grand totals
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }
        // Proceeding to apply formatting
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";
        workbook.SaveAs("PivotReport.xlsx");
    }
}
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

因此,到2025年,这就是如何创建数据透视表。

输出

如何使用 C# Interop 与 IronXL 在 Excel 中创建数据透视表:图 1 - 原始 Excel 文件与我们创建的数据透视表,显示了单元格。

如何使用IronXL公式创建动态摘要?

对于需要动态更新(类似于创建数据透视表)的场景,IronXL 可以利用 Excel 的内置公式来计算数据透视表刷新功能,从而获得答案。 一般来说,这比之前的答案更好——您的记录将以更现代、更优雅的方式进行处理,使用的代码也更容易理解。 设置简单,无需联系客服或阅读手册。

// Load the workbook
WorkBook workbook = WorkBook.Load(inputPath);
// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet";
// Convert worksheet to DataTable
DataTable dataTable = dataSheet.ToDataTable(true);
// Create new summary worksheet
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");
// Get unique product-region combinations
var uniqueCombos = dataTable.AsEnumerable()
    .Select(row => new {
        Product = row["Product"].ToString(),
        Region = row["Region"].ToString()
    })
    .Distinct()
    .OrderBy(x => x.Product)
    .ThenBy(x => x.Region);
// Add header row
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";
// Populate rows with formulas
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;
    // Adjust column references if your Sales column is C (not D)
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    rowIndex++;
}
// Optional: add total row
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";
// Save output file
workbook.SaveAs(outputPath);  //filename
// Load the workbook
WorkBook workbook = WorkBook.Load(inputPath);
// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet";
// Convert worksheet to DataTable
DataTable dataTable = dataSheet.ToDataTable(true);
// Create new summary worksheet
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");
// Get unique product-region combinations
var uniqueCombos = dataTable.AsEnumerable()
    .Select(row => new {
        Product = row["Product"].ToString(),
        Region = row["Region"].ToString()
    })
    .Distinct()
    .OrderBy(x => x.Product)
    .ThenBy(x => x.Region);
// Add header row
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";
// Populate rows with formulas
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;
    // Adjust column references if your Sales column is C (not D)
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    rowIndex++;
}
// Optional: add total row
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";
// Save output file
workbook.SaveAs(outputPath);  //filename
IRON VB CONVERTER ERROR developers@ironsoftware.com
$vbLabelText   $csharpLabel

这些公式与源数据保持实时连接,当数据表更改时自动更新 - 类似于数据透视表刷新行为,但没有Interop依赖。

如果我们将这段代码应用到上一个示例中的示例 Excel 文件(XLS / XLSX),页面上将显示以下字段:

如何在 Excel 中使用 C# Interop 与 IronXL 创建数据透视表:图 2 - 注意:C# 动态汇总输出在位置上下文中可见。

比较 C# Interop 和 IronXL 在数据透视表方面的性能

方面 C# 互操作 IronXL
办公室要求 是的 - 完全安装 否 - 独立库
平台支持 仅限 Windows 系统 Windows、Linux、macOS、Docker
内存管理 需要手动清理 COM 对象 自动 .NET 垃圾回收
部署 复杂 - 办公许可 简单 - 单个 DLL
表现 Excel进程启动缓慢 快速内存计算
云端兼容 否 - Azure 限制 是的 - 支持 Azure Functions
原生透视表 无 - 聚合替代方案
发展速度 速度慢 - COM 复杂性 快速直观的 API

你应该选择哪种方法?

选择 C# 互操作的条件:

  • 必须使用原生 Excel 数据透视表对象
  • 仅在 Windows 系统上进行开发,且每台目标机器都已安装 Office 办公软件。
  • 仅部署到您管理的桌面系统
  • 现有遗留代码依赖于互操作性
  • 使用旧版 .NET Framework

选择 IronXL 的情况:

  • 部署到服务器或云环境和服务(Azure、AWS 等) 构建跨平台应用程序
  • 要求更高的性能和可靠性
  • 避免办公软件许可费用 需要更简洁、更易于维护的代码
  • 支持 Mac、iOS、Android 和/或 Linux 系统
  • 熟悉现代 .NET Framework Core 和 .NET 5、6、7、8、9、10。
  • 您需要在开发控制台/IDE 中拥有完全访问权限,以便根据数据范围以编程方式配置透视表中每个字段的设置。

结论

虽然C# Excel Interop可以开发本地数据透视表,但其部署限制和复杂性使其对于现代应用程序来说越来越不现实。 IronXL通过数据聚合和基于公式的汇总提供强大的替代方案,消除了对 Office 的依赖,同时保持了分析能力。 如果本文对您有帮助,请投票、评论或订阅。

对于正在寻找无需 Interop 即可开发透视表的替代方案的开发人员来说,IronXL 提供了一种更优的途径,避免了 COM 的复杂性,可在所有平台上运行,并简化了部署。 虽然没有本机数据透视对象的权衡通过更大的灵活性、更好的性能和消除Office许可要求得以补偿。

立即开始使用 IronXL。
green arrow pointer

准备好现代化您的Excel自动化并在现代C#中创建自己的数据透视表代码了吗?

通过 NuGet 包管理器,您可以在几秒钟内将 IronXL 集成到您的 C# 应用程序中。 您可以试用免费试用版购买 IronXL 许可证,以消除生产应用程序中的互操作依赖关系。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。