
如何使用IronXL在 Excel 中使用 C# 创建数据透视表
在 Excel 中以编程方式生成数据透视表需要使用 C# 互操作 及其 Office 依赖项,或者使用像IronXL这样可以独立工作的现代库——本教程演示了这两种方法,并向您展示了为什么现代方法才是更好的选择。
历史上,在服务器端或跨平台.NET代码中构建透视表一直是一件很痛苦的事情。 传统的 COM 互操作方式会将您绑定到安装了完整 Office 的 Windows 机器上,如果您错过一次 COM 清理调用,就会产生内存泄漏,并且一旦尝试部署到 Linux 或 Docker 容器,就会崩溃。 现代的替代方案——使用IronXL和 LINQ 编写聚合逻辑——可以在任何.NET运行的地方运行,不需要 Office 许可证,并且可以提供简洁、易读的代码。
本指南将详细介绍这两种技术。 您将看到原始的互操作方法,了解它究竟脆弱在哪里,然后使用IronXL在 C# 中构建相同的透视式汇总表。 您还将看到如何使用 Excel 公式实现实时、自动更新的汇总,其行为类似于真正的数据透视表刷新。
什么是Excel数据透视表?
数据透视表是电子表格软件中最强大的分析工具之一。 它通过分组行、汇总值并将结果投影为交叉表布局来总结大型数据集——而无需您手动编写任何公式。 微软官方的透视表文档对 Excel 内部的透视表功能进行了全面的概述。
数据透视表出现在 Microsoft Excel、Google Sheets、Apple Numbers 和大多数其他电子表格工具中。 核心概念始终相同:您定义行字段、列字段和值字段,该工具将为您构建汇总矩阵。 当基础数据发生变化时,刷新透视表,汇总信息会自动更新。
在 C# 服务器端代码中,您主要有两种选择:
- C# 互操作-- 通过 COM 自动执行正在运行的 Excel 进程,从而在 XLSX 文件中创建真正的原生数据透视表对象 IronXL与 LINQ 聚合——将工作簿读入内存,在托管的.NET代码中计算相同的摘要,并将结果写入新的工作表。
两种方法都能产生有用的结果。 但只有其中一种能在现代部署环境中可靠运行。
如何使用C#互操作创建数据透视表?
C# Excel Interop 使您能够通过 COM 自动化直接访问 Excel 的原生数据透视表功能。 您创建一个Excel.Application对象,打开一个工作簿,定义一个指向数据范围的数据透视缓存,然后配置行字段、列字段和数据字段。
如何设置互操作透视表代码
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);Imports Excel = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices
' Create Excel application instance
Dim xlApp As New Excel.Application()
Dim xlWorkbook As Excel.Workbook = xlApp.Workbooks.Open("C:\Data\SalesData.xlsx")
Dim xlSheet As Excel.Worksheet = CType(xlWorkbook.Sheets(1), Excel.Worksheet)
Dim xlPivotSheet As Excel.Worksheet = CType(xlWorkbook.Sheets.Add(), Excel.Worksheet)
' Define data range for pivot table
Dim dataRange As Excel.Range = xlSheet.UsedRange
' Create pivot cache and pivot table
Dim pivotCache As Excel.PivotCache = xlWorkbook.PivotCaches().Create(Excel.XlPivotTableSourceType.xlDatabase, dataRange, Type.Missing)
Dim pivotTable As Excel.PivotTable = pivotCache.CreatePivotTable(xlPivotSheet.Cells(3, 1), "SalesPivot", Type.Missing, Type.Missing)
' Configure pivot table fields
Dim productField As Excel.PivotField = CType(pivotTable.PivotFields("Product"), Excel.PivotField)
productField.Orientation = Excel.XlPivotFieldOrientation.xlRowField
productField.Position = 1
Dim regionField As Excel.PivotField = CType(pivotTable.PivotFields("Region"), Excel.PivotField)
regionField.Orientation = Excel.XlPivotFieldOrientation.xlColumnField
regionField.Position = 1
Dim salesField As Excel.PivotField = CType(pivotTable.PivotFields("Sales"), Excel.PivotField)
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)此 Interop 示例创建了一个原生 Excel 数据透视表,其中产品为行,地区为列,销售额汇总在数据区域中。 虽然它确实在 XLSX 文件中生成了一个真正的透视表对象,但这需要安装 Microsoft Office,并且需要仔细管理 COM 对象。 如果错过一次Marshal.ReleaseComObject调用,您将发现任务管理器中积累的过时Excel进程。
如何在编写任何代码之前安装IronXL
在采用IronXL方法之前,请先通过NuGet包管理器安装该库:
服务器、容器或开发机器上无需安装 Office。IronXL 完全在托管的.NET内存中读取和写入IronXL 、XLS 和 CSV 文件。
C# Interop产生了什么问题?
互操作性方法存在一些重大挑战,这些挑战在实际部署中会迅速加剧。 Stack Overflow 和其他编程资源仍然推荐它,因为许多帖子写于 2000 年代初期,并且后来被锁定——所以这些建议已经停滞不前。
部署依赖项——运行您的代码的每台机器都必须安装已授权的 Microsoft Office 副本,包括生产服务器和 CI/CD 构建代理。 这既增加了许可成本,也增加了部署复杂性,而这些问题完全可以通过现代替代方案避免。
内存管理负担 -- 必须使用Marshal.ReleaseComObject()显式释放COM对象。 即使缺少一个对象也会导致 Excel 进程无限期地在内存中挂起, Stack Overflow 上对此有大量记录。 对于长时间运行的服务或ASP.NET Web 应用程序而言,这会造成严重的资源泄漏。
平台限制——互操作功能仅适用于安装了 Office 的 Windows 系统。 它无法在 Linux、macOS、Docker 容器或 Azure Functions 或 AWS Lambda 等无服务器平台上运行。 这会完全阻碍你使用现代云原生架构。
性能瓶颈——启动 Excel 应用程序实例速度慢且资源消耗大。对于需要生成数十甚至数百份报表的服务器端批量处理,这种启动开销会成为严重的吞吐量瓶颈。
版本兼容性脆弱性——不同的 Office 版本提供的 COM 接口略有不同。 适用于 Office 2019 的代码在 Office 2016 或 Microsoft 365 中可能表现不同,而且您无法在部署中固定版本。 微软关于 Office Interop 程序集的文档指出,这些版本控制限制是一个已知的限制。
CI/CD 不兼容——大多数持续集成环境都没有安装 Office。 测试数据透视表生成代码需要模拟整个 COM 层,或者维护一个具有已授权 Office 安装的特殊 Windows 代理。
对于任何面向.NET 6 或更高版本(包括.NET 10)的新.NET应用程序,这些限制使得互操作成为一种不切实际的选择。
IronXL如何在不使用互操作的情况下创建数据透视表?
IronXL创建数据透视表的方法有所不同。 IronXL不是通过 COM 控制外部 Excel 进程,而是将工作簿读入托管的.NET内存,使您可以直接访问单元格值、公式和工作表结构。 然后,您可以使用标准 LINQ 查询构建透视表式聚合,并将结果写回新的工作表。
如何使用IronXL和 LINQ 构建交叉表汇总
以下示例加载销售数据工作簿,计算按地区划分的产品交叉表,并将汇总结果写入新工作表——所有这些都无需任何 Office 依赖项:
using IronXL;
using System.Linq;
using System.Data;
// Load Excel file -- no Office installation required
WorkBook workbook = WorkBook.Load("SalesData.xlsx");
WorkSheet dataSheet = workbook.WorkSheets[0];
// Convert to DataTable for flexible LINQ manipulation
var dataTable = dataSheet.ToDataTable(true); // true = first row as column headers
// Build pivot-style aggregation using LINQ grouping
var pivotData = dataTable.AsEnumerable()
.GroupBy(row => new {
Product = row["Product"].ToString(),
Region = row["Region"].ToString()
})
.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 the pivot report worksheet
WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");
// Get distinct row and column values
var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p).ToList();
var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r).ToList();
// Write column headers
pivotSheet["A1"].Value = "Product / Region";
for (int c = 0; c < regions.Count; c++)
{
pivotSheet[$"{(char)('B' + c)}1"].Value = regions[c];
}
// Populate data rows
for (int r = 0; r < products.Count; r++)
{
pivotSheet[$"A{r + 2}"].Value = products[r];
for (int c = 0; c < regions.Count; c++)
{
var sales = pivotData
.Where(p => p.Product == products[r] && p.Region == regions[c])
.Select(p => p.TotalSales)
.FirstOrDefault();
pivotSheet[$"{(char)('B' + c)}{r + 2}"].Value = sales;
}
}
// Add a totals row using Excel SUM formulas
int totalRow = products.Count + 2;
pivotSheet[$"A{totalRow}"].Value = "Total";
for (int c = 0; c < regions.Count; c++)
{
char col = (char)('B' + c);
pivotSheet[$"{col}{totalRow}"].Formula = $"=SUM({col}2:{col}{totalRow - 1})";
}
// Apply currency formatting to the data range
var dataRange = pivotSheet[$"B2:{(char)('B' + regions.Count - 1)}{totalRow}"];
dataRange.FormatString = "$#,##0.00";
workbook.SaveAs("PivotReport.xlsx");Imports IronXL
Imports System.Linq
Imports System.Data
' Load Excel file -- no Office installation required
Dim workbook As WorkBook = WorkBook.Load("SalesData.xlsx")
Dim dataSheet As WorkSheet = workbook.WorkSheets(0)
' Convert to DataTable for flexible LINQ manipulation
Dim dataTable As DataTable = dataSheet.ToDataTable(True) ' True = first row as column headers
' Build pivot-style aggregation using LINQ grouping
Dim pivotData = dataTable.AsEnumerable() _
.GroupBy(Function(row) New With {
Key .Product = row("Product").ToString(),
Key .Region = row("Region").ToString()
}) _
.Select(Function(g) New With {
Key .Product = g.Key.Product,
Key .Region = g.Key.Region,
Key .TotalSales = g.Sum(Function(row) Convert.ToDecimal(row("Sales"))),
Key .AverageSale = g.Average(Function(row) Convert.ToDecimal(row("Sales"))),
Key .Count = g.Count()
})
' Create the pivot report worksheet
Dim pivotSheet As WorkSheet = workbook.CreateWorkSheet("PivotReport")
' Get distinct row and column values
Dim products = pivotData.Select(Function(p) p.Product).Distinct().OrderBy(Function(p) p).ToList()
Dim regions = pivotData.Select(Function(p) p.Region).Distinct().OrderBy(Function(r) r).ToList()
' Write column headers
pivotSheet("A1").Value = "Product / Region"
For c As Integer = 0 To regions.Count - 1
pivotSheet($"{ChrW(AscW("B"c) + c)}1").Value = regions(c)
Next
' Populate data rows
For r As Integer = 0 To products.Count - 1
pivotSheet($"A{r + 2}").Value = products(r)
For c As Integer = 0 To regions.Count - 1
Dim sales = pivotData _
.Where(Function(p) p.Product = products(r) AndAlso p.Region = regions(c)) _
.Select(Function(p) p.TotalSales) _
.FirstOrDefault()
pivotSheet($"{ChrW(AscW("B"c) + c)}{r + 2}").Value = sales
Next
Next
' Add a totals row using Excel SUM formulas
Dim totalRow As Integer = products.Count + 2
pivotSheet($"A{totalRow}").Value = "Total"
For c As Integer = 0 To regions.Count - 1
Dim col As Char = ChrW(AscW("B"c) + c)
pivotSheet($"{col}{totalRow}").Formula = $"=SUM({col}2:{col}{totalRow - 1})"
Next
' Apply currency formatting to the data range
Dim dataRange = pivotSheet($"B2:{ChrW(AscW("B"c) + regions.Count - 1)}{totalRow}")
dataRange.FormatString = "$#,##0.00"
workbook.SaveAs("PivotReport.xlsx")这样就能生成与 Excel 原生数据透视表相同的交叉汇总表。 您可以完全通过编程方式控制每个单元格、公式和格式字符串,而无需清理 COM 对象。

如何使用Excel公式创建动态汇总?
如果您希望汇总表保持实时更新(即源数据更改时自动重新计算), IronXL允许您直接在单元格中写入 Excel 公式字符串。 这为您提供了类似于数据透视表的自动刷新行为,无需任何互操作依赖。
这里的关键功能是COUNTIFS。 SUMIFS基于多个条件列条件汇总一个范围; COUNTIFS计算匹配的行。 两者都接受对已命名工作表的引用,因此您可以按名称将汇总表直接指向源数据表。
如何使用IronXL编写基于公式的聚合
using IronXL;
using System.Data;
string inputPath = "SalesData.xlsx";
string outputPath = "DynamicSummary.xlsx";
WorkBook workbook = WorkBook.Load(inputPath);
WorkSheet dataSheet = workbook.WorkSheets[0];
// Name the data sheet so formula references are stable
dataSheet.Name = "DataSheet";
// Convert to DataTable to enumerate unique product/region combinations
DataTable dataTable = dataSheet.ToDataTable(true);
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");
// Get unique product-region pairs
var uniqueCombos = dataTable.AsEnumerable()
.Select(row => new {
Product = row["Product"].ToString(),
Region = row["Region"].ToString()
})
.Distinct()
.OrderBy(x => x.Product)
.ThenBy(x => x.Region)
.ToList();
// Header row
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";
// Populate rows with live SUMIFS / COUNTIFS formulas
for (int i = 0; i < uniqueCombos.Count; i++)
{
int rowIndex = i + 2;
var combo = uniqueCombos[i];
summarySheet[$"A{rowIndex}"].Value = combo.Product;
summarySheet[$"B{rowIndex}"].Value = combo.Region;
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}\")";
}
// Grand total row
int totalRow = uniqueCombos.Count + 2;
summarySheet[$"A{totalRow}"].Value = "Total";
summarySheet[$"C{totalRow}"].Formula = $"=SUM(C2:C{totalRow - 1})";
summarySheet[$"D{totalRow}"].Formula = $"=SUM(D2:D{totalRow - 1})";
workbook.SaveAs(outputPath);Imports IronXL
Imports System.Data
Imports System.Linq
Dim inputPath As String = "SalesData.xlsx"
Dim outputPath As String = "DynamicSummary.xlsx"
Dim workbook As WorkBook = WorkBook.Load(inputPath)
Dim dataSheet As WorkSheet = workbook.WorkSheets(0)
' Name the data sheet so formula references are stable
dataSheet.Name = "DataSheet"
' Convert to DataTable to enumerate unique product/region combinations
Dim dataTable As DataTable = dataSheet.ToDataTable(True)
Dim summarySheet As WorkSheet = workbook.CreateWorkSheet("DynamicSummary")
' Get unique product-region pairs
Dim uniqueCombos = dataTable.AsEnumerable() _
.Select(Function(row) New With {
.Product = row("Product").ToString(),
.Region = row("Region").ToString()
}) _
.Distinct() _
.OrderBy(Function(x) x.Product) _
.ThenBy(Function(x) x.Region) _
.ToList()
' Header row
summarySheet("A1").Value = "Product"
summarySheet("B1").Value = "Region"
summarySheet("C1").Value = "Total Sales"
summarySheet("D1").Value = "Count"
' Populate rows with live SUMIFS / COUNTIFS formulas
For i As Integer = 0 To uniqueCombos.Count - 1
Dim rowIndex As Integer = i + 2
Dim combo = uniqueCombos(i)
summarySheet($"A{rowIndex}").Value = combo.Product
summarySheet($"B{rowIndex}").Value = combo.Region
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}"")"
Next
' Grand total row
Dim totalRow As Integer = uniqueCombos.Count + 2
summarySheet($"A{totalRow}").Value = "Total"
summarySheet($"C{totalRow}").Formula = $"=SUM(C2:C{totalRow - 1})"
summarySheet($"D{totalRow}").Formula = $"=SUM(D2:D{totalRow - 1})"
workbook.SaveAs(outputPath)这些公式与源数据保持实时连接。 当有人在DataSheet中更新一个值时,Excel在下次打开或刷新时自动重新计算汇总,从而为您提供与本机数据透视表刷新周期相同的行为,而无需COM自动化。
将此方法应用于上例中使用的同一销售数据工作簿时,输出结果如下所示:

这种基于公式的方法还允许您使用IronXL 的单元格格式 API向汇总单元格添加条件格式、数据条或图标集,从而使您的报告在视觉上清晰明了,而无需在 Excel 用户界面中进行任何手动操作。
这两种方法有何异同?
在选择方法之前,权衡利弊很有帮助。下表涵盖了生产环境.NET开发中最重要的几个方面:
| 方面 | C# 互操作 | IronXL |
|---|---|---|
| 办公室要求 | 是的——每台机器都要完整安装。 | 否——独立的NuGet包 |
| 平台支持 | 仅限 Windows | Windows、Linux、macOS、Docker |
| 内存管理 | 需要手动清理 COM 对象 | 自动 .NET 垃圾回收 |
| 部署复杂性 | 高价 -- 办公软件许可 + 安装 | 低——单个 DLL 引用 |
| 表现 | 速度慢——Excel进程启动开销 | 快速——内存内计算 |
| 云兼容 | 否——在 Azure Functions 和 AWS Lambda 上均已阻止 | 是的——可在任何云平台上运行 |
| 原生透视表对象 | 是的——完整的Excel数据透视表 | 否——基于聚合的等效项 |
| 发展速度 | 速度慢——COM 复杂性 | 快速流畅的托管 API |
| .NET 10 支持 | 有限的 COM 绑定问题 | 完整版——面向现代.NET |
Interop 唯一具有明显优势的情况是,当您特别需要将原生 Excel 数据透视表对象嵌入到 XLSX 文件中时——例如,如果下游用户必须使用 Excel 内置的数据透视表 UI 与之交互(向下钻取、筛选、交互式地更改聚合函数)。 在其他所有情况下,IronXL 的方法编写速度更快、部署更容易、可移植性也更强。
你应该选择哪种方法?
正确的选择取决于您的部署环境和用户需求。
仅在以下情况下选择C# 互操作:
您的用户需要能够在 Excel 用户界面中交互式操作的原生 Excel 数据透视表对象。 您的目标是一个封闭的 Windows 桌面环境,其中每台机器都保证安装了 Office。 您维护的旧版.NET Framework代码已经依赖于 Interop,目前重写并不合理。
选择IronXL当:
- 你要部署到服务器、容器或任何云环境(Azure、AWS、GCP) 你需要对Linux、macOS或基于Docker的构建提供跨平台支持。 您想要简洁、易于维护的代码,而无需 COM 生命周期管理。
- 您面向的是.NET 5、6、7、8、9 或 10。 您希望避免在服务器基础架构上支付 Microsoft Office 许可费用 您需要快速批量处理多个工作簿,而无需逐个启动 Excel 进程。
对于绝大多数现代.NET应用程序而言, IronXL是切实可行的选择。它基于聚合的输出能够满足所有实际的报表需求,并且还能带来完全的可移植性。
您可以在IronXL文档和IronXL示例库中探索更多功能,包括单元格格式设置、公式计算、数据验证和图表生成。
如何立即开始使用IronXL ?
IronXL库可在NuGet上获取,只需不到一分钟即可添加到任何.NET项目中:
安装完成后,您可以使用简洁、文档齐全的 API 加载现有工作簿或创建新工作簿,读取和写入单元格值,应用公式,设置格式字符串,并保存为 XLSX。 它不依赖 COM,不依赖 Office,也不需要特殊的服务器配置。
有关完整的 API 文档,请参阅IronXL入门指南、 C# Excel 互操作迁移指南和IronXL代码示例。 您还可以通过IronXL对比文章将IronXL与其他 Excel 库进行比较。
免费试用许可证允许您在正式提交之前,在自己的项目中测试所有功能。 当您准备部署到生产环境时,商业版IronXL许可证将移除试用水印并包含优先支持。 首先体验免费试用版,看看跨平台 Excel 自动化可以变得多么简单。

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



