
如何在不使用互操作的情况下,使用 C# 在 Excel 中创建数据透视表
**使用 C# 在 Excel 中创建数据透视表非常简单,只需选择合适的库即可——Excel互操作性 需要每台机器上都安装 Office,而IronXL可以在任何运行.NET 的环境中运行。**本指南将通过完整的代码示例详细介绍这两种方法,涵盖设置、数据聚合、字段配置和部署注意事项,以便您根据项目需求做出正确的选择。

Excel互操作性 和IronXL有什么区别?
在编写任何一行代码之前,了解每种方法的底层原理会很有帮助。 Excel互操作性 使用COM(组件对象模型)直接从 C# 驱动 Microsoft Excel。 .NET进程会与正在运行的 Excel 实例通信,这意味着计算机上必须安装 Excel。您操作的每个对象(工作簿、工作表、区域、透视缓存)都是 COM 包装器,必须显式释放每个包装器,以避免进程泄漏。
IronXL采取了一条截然不同的道路。 它直接读取和写入Open XML 文件格式,完全无需启动 Excel。 最终得到的是一个标准的.NET库,它具有熟悉的对象模式、垃圾回收内存,并且不依赖于 Office 安装或许可。 这种架构使得IronXL适用于服务器端工作负载、Docker 容器、Linux 主机以及任何.NET 6、 .NET 8 或.NET 10 环境。
| 标准 | Excel互操作性 | IronXL |
|---|---|---|
| 办公室要求 | 是 | 无 |
| 仅限 Windows | 是 | 否——跨平台 |
| 内存管理 | 手动 COM 版本 | 自动(.NET GC) |
| 服务器部署 | 难的 | 直截了当 |
| 原生透视表 | 完整的 Excel 透视表 API | 通过 LINQ 进行数据聚合 |
| 需要许可证 | 办公许可证 | IronXL仅限授权 |

如何安装各个库?
设置 Excel互操作性
Excel互操作性 以NuGet包的形式提供。 在您的项目中运行以下任一命令:
Install-Package Microsoft.Office.Interop.Excel
dotnet add package Microsoft.Office.Interop.Excel
请注意,仅此软件包是不够的——目标计算机必须安装匹配版本的 Microsoft Excel 并已获得正确的许可。 这种依赖关系排除了大多数服务器、容器和云场景。

设置IronXL
通过NuGet安装IronXL ,无需其他系统要求:
安装完成后,您可以在任何支持.NET 的平台上立即开始读取和写入 Excel 文件。 无需Office许可证,无需COM注册,无需服务器配置。 有关其他选项,包括离线安装和项目参考设置,请参阅IronXL安装指南。

如何使用C#互操作创建Excel数据透视表?
Interop数据透视表工作流遵循严格的顺序:从源范围创建数据透视缓存,然后在单独的工作表上构建PivotTable对象,然后配置字段方向。 以下示例使用与.NET 10 兼容的顶级 C# 语句:
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
// Launch Excel and set up workbooks
var excelApp = new Excel.Application();
excelApp.Visible = false;
var workbook = excelApp.Workbooks.Add();
var dataSheet = (Excel.Worksheet)workbook.Worksheets[1];
dataSheet.Name = "SalesData";
var pivotSheet = (Excel.Worksheet)workbook.Worksheets.Add();
pivotSheet.Name = "Pivot";
// Populate header row
dataSheet.Cells[1, 1] = "Product";
dataSheet.Cells[1, 2] = "Region";
dataSheet.Cells[1, 3] = "Sales";
// Add sample data rows
object[,] rows = {
{ "Laptop", "North", 1200 },
{ "Laptop", "South", 1500 },
{ "Phone", "North", 800 },
{ "Phone", "South", 950 },
{ "Tablet", "East", 600 },
{ "Tablet", "West", 750 },
{ "Monitor", "North", 400 },
{ "Monitor", "South", 500 },
{ "Keyboard", "East", 300 },
};
for (int i = 0; i < rows.GetLength(0); i++)
{
dataSheet.Cells[i + 2, 1] = rows[i, 0];
dataSheet.Cells[i + 2, 2] = rows[i, 1];
dataSheet.Cells[i + 2, 3] = rows[i, 2];
}
// Build pivot cache from source range
Excel.Range dataRange = dataSheet.Range["A1:C10"];
Excel.PivotCache pivotCache = workbook.PivotCaches().Create(
Excel.XlPivotTableSourceType.xlDatabase, dataRange);
// Create the PivotTable on the pivot sheet
Excel.PivotTables pivotTables = (Excel.PivotTables)pivotSheet.PivotTables();
Excel.PivotTable pivotTable = pivotTables.Add(
pivotCache, pivotSheet.Range["A3"], "SalesPivot");
// Assign field orientations
((Excel.PivotField)pivotTable.PivotFields("Product")).Orientation =
Excel.XlPivotFieldOrientation.xlRowField;
((Excel.PivotField)pivotTable.PivotFields("Region")).Orientation =
Excel.XlPivotFieldOrientation.xlColumnField;
((Excel.PivotField)pivotTable.PivotFields("Sales")).Orientation =
Excel.XlPivotFieldOrientation.xlDataField;
// Enable grand totals
pivotTable.RowGrand = true;
pivotTable.ColumnGrand = true;
// Save and close
workbook.SaveAs(@"C:\output\pivot_interop.xlsx");
workbook.Close(false);
excelApp.Quit();
// Release every COM object -- skipping any of these causes Excel to stay in memory
Marshal.ReleaseComObject(pivotTable);
Marshal.ReleaseComObject(pivotTables);
Marshal.ReleaseComObject(pivotCache);
Marshal.ReleaseComObject(dataRange);
Marshal.ReleaseComObject(pivotSheet);
Marshal.ReleaseComObject(dataSheet);
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(excelApp);Imports Excel = Microsoft.Office.Interop.Excel
Imports System.Runtime.InteropServices
' Launch Excel and set up workbooks
Dim excelApp As New Excel.Application()
excelApp.Visible = False
Dim workbook As Excel.Workbook = excelApp.Workbooks.Add()
Dim dataSheet As Excel.Worksheet = CType(workbook.Worksheets(1), Excel.Worksheet)
dataSheet.Name = "SalesData"
Dim pivotSheet As Excel.Worksheet = CType(workbook.Worksheets.Add(), Excel.Worksheet)
pivotSheet.Name = "Pivot"
' Populate header row
dataSheet.Cells(1, 1) = "Product"
dataSheet.Cells(1, 2) = "Region"
dataSheet.Cells(1, 3) = "Sales"
' Add sample data rows
Dim rows As Object(,) = {
{"Laptop", "North", 1200},
{"Laptop", "South", 1500},
{"Phone", "North", 800},
{"Phone", "South", 950},
{"Tablet", "East", 600},
{"Tablet", "West", 750},
{"Monitor", "North", 400},
{"Monitor", "South", 500},
{"Keyboard", "East", 300}
}
For i As Integer = 0 To rows.GetLength(0) - 1
dataSheet.Cells(i + 2, 1) = rows(i, 0)
dataSheet.Cells(i + 2, 2) = rows(i, 1)
dataSheet.Cells(i + 2, 3) = rows(i, 2)
Next
' Build pivot cache from source range
Dim dataRange As Excel.Range = dataSheet.Range("A1:C10")
Dim pivotCache As Excel.PivotCache = workbook.PivotCaches().Create(Excel.XlPivotTableSourceType.xlDatabase, dataRange)
' Create the PivotTable on the pivot sheet
Dim pivotTables As Excel.PivotTables = CType(pivotSheet.PivotTables(), Excel.PivotTables)
Dim pivotTable As Excel.PivotTable = pivotTables.Add(pivotCache, pivotSheet.Range("A3"), "SalesPivot")
' Assign field orientations
CType(pivotTable.PivotFields("Product"), Excel.PivotField).Orientation = Excel.XlPivotFieldOrientation.xlRowField
CType(pivotTable.PivotFields("Region"), Excel.PivotField).Orientation = Excel.XlPivotFieldOrientation.xlColumnField
CType(pivotTable.PivotFields("Sales"), Excel.PivotField).Orientation = Excel.XlPivotFieldOrientation.xlDataField
' Enable grand totals
pivotTable.RowGrand = True
pivotTable.ColumnGrand = True
' Save and close
workbook.SaveAs("C:\output\pivot_interop.xlsx")
workbook.Close(False)
excelApp.Quit()
' Release every COM object -- skipping any of these causes Excel to stay in memory
Marshal.ReleaseComObject(pivotTable)
Marshal.ReleaseComObject(pivotTables)
Marshal.ReleaseComObject(pivotCache)
Marshal.ReleaseComObject(dataRange)
Marshal.ReleaseComObject(pivotSheet)
Marshal.ReleaseComObject(dataSheet)
Marshal.ReleaseComObject(workbook)
Marshal.ReleaseComObject(excelApp)每个COM封装器需要匹配的Marshal.ReleaseComObject调用。 即使缺少一个引用,也会导致 Excel 进程在后台持续运行,消耗内存和文件句柄,直到宿主进程结束。 清理工作量会随着电子表格操作的复杂程度而增加。
如何用IronXL达到同样的效果?
IronXL不像 Interop 那样公开原生透视表 API——Open XML 透视表格式有数百个 XML 属性,大多数业务需求可以通过用纯 C# 编写的显式 LINQ 聚合更可靠地满足。 输出结果是一个干净的汇总表,当文件打开时,该汇总表能够正确地重新计算,而无需依赖 Excel 刷新数据透视表缓存。
using IronXL;
using System.Data;
// Create workbook and populate source data
WorkBook workbook = WorkBook.Create(ExcelFileFormat.XLSX);
WorkSheet dataSheet = workbook.CreateWorkSheet("SalesData");
dataSheet["A1"].Value = "Product";
dataSheet["B1"].Value = "Region";
dataSheet["C1"].Value = "Sales";
object[,] rows = {
{ "Laptop", "North", 1200 },
{ "Laptop", "South", 1500 },
{ "Phone", "North", 800 },
{ "Phone", "South", 950 },
{ "Tablet", "East", 600 },
{ "Tablet", "West", 750 },
{ "Monitor", "North", 400 },
{ "Monitor", "South", 500 },
{ "Keyboard", "East", 300 },
};
for (int i = 0; i < rows.GetLength(0); i++)
{
dataSheet[$"A{i + 2}"].Value = rows[i, 0];
dataSheet[$"B{i + 2}"].Value = rows[i, 1];
dataSheet[$"C{i + 2}"].Value = rows[i, 2];
}
// Aggregate data with LINQ -- equivalent to a pivot table row/column/value layout
DataTable table = dataSheet["A1:C10"].ToDataTable(true);
var summary = table.AsEnumerable()
.GroupBy(row => row.Field<string>("Product"))
.Select((group, idx) => new
{
Product = group.Key,
TotalSales = group.Sum(r => Convert.ToDecimal(r["Sales"])),
RegionCount = group.Select(r => r.Field<string>("Region")).Distinct().Count(),
RowIndex = idx + 2
})
.OrderByDescending(x => x.TotalSales);
// Write the summary sheet
WorkSheet summarySheet = workbook.CreateWorkSheet("Summary");
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Total Sales";
summarySheet["C1"].Value = "Regions";
foreach (var item in summary)
{
summarySheet[$"A{item.RowIndex}"].Value = item.Product;
summarySheet[$"B{item.RowIndex}"].Value = (double)item.TotalSales;
summarySheet[$"C{item.RowIndex}"].Value = item.RegionCount;
}
// Apply currency format to the sales column
summarySheet["B:B"].FormatString = "$#,##0.00";
// Bold the header row
summarySheet["A1:C1"].Style.Font.Bold = true;
// Save -- no COM cleanup needed
workbook.SaveAs(@"C:\output\analysis_ironxl.xlsx");Imports IronXL
Imports System.Data
Imports System.Linq
' Create workbook and populate source data
Dim workbook As WorkBook = WorkBook.Create(ExcelFileFormat.XLSX)
Dim dataSheet As WorkSheet = workbook.CreateWorkSheet("SalesData")
dataSheet("A1").Value = "Product"
dataSheet("B1").Value = "Region"
dataSheet("C1").Value = "Sales"
Dim rows As Object(,) = {
{"Laptop", "North", 1200},
{"Laptop", "South", 1500},
{"Phone", "North", 800},
{"Phone", "South", 950},
{"Tablet", "East", 600},
{"Tablet", "West", 750},
{"Monitor", "North", 400},
{"Monitor", "South", 500},
{"Keyboard", "East", 300}
}
For i As Integer = 0 To rows.GetLength(0) - 1
dataSheet($"A{i + 2}").Value = rows(i, 0)
dataSheet($"B{i + 2}").Value = rows(i, 1)
dataSheet($"C{i + 2}").Value = rows(i, 2)
Next
' Aggregate data with LINQ -- equivalent to a pivot table row/column/value layout
Dim table As DataTable = dataSheet("A1:C10").ToDataTable(True)
Dim summary = table.AsEnumerable() _
.GroupBy(Function(row) row.Field(Of String)("Product")) _
.Select(Function(group, idx) New With {
.Product = group.Key,
.TotalSales = group.Sum(Function(r) Convert.ToDecimal(r("Sales"))),
.RegionCount = group.Select(Function(r) r.Field(Of String)("Region")).Distinct().Count(),
.RowIndex = idx + 2
}) _
.OrderByDescending(Function(x) x.TotalSales)
' Write the summary sheet
Dim summarySheet As WorkSheet = workbook.CreateWorkSheet("Summary")
summarySheet("A1").Value = "Product"
summarySheet("B1").Value = "Total Sales"
summarySheet("C1").Value = "Regions"
For Each item In summary
summarySheet($"A{item.RowIndex}").Value = item.Product
summarySheet($"B{item.RowIndex}").Value = CDbl(item.TotalSales)
summarySheet($"C{item.RowIndex}").Value = item.RegionCount
Next
' Apply currency format to the sales column
summarySheet("B:B").FormatString = "$#,##0.00"
' Bold the header row
summarySheet("A1:C1").Style.Font.Bold = True
' Save -- no COM cleanup needed
workbook.SaveAs("C:\output\analysis_ironxl.xlsx")无需释放 COM 对象,无需终止 Excel 进程,也无需管理 Office 许可证。 同一段代码无需修改即可在 Linux、macOS 和 Windows 上编译运行。 有关聚合 API 的更多详细信息,请访问IronXL WorkSheet 文档。
输出


部署和维护方面的主要区别是什么?
部署要求
部署基于互操作性的应用程序需要验证目标环境是否运行 Windows,是否安装了正确的 Office 版本,以及服务器上是否配置了 COM 自动化权限。 云托管环境和容器化工作负载通常无法在不添加虚拟化 Windows 桌面基础架构的情况下满足这些要求,但这会显著增加成本和操作复杂性。
IronXL没有这样的限制。 添加NuGet包,将目标框架设置为.NET 6 或更高版本,应用程序即可部署到任何主机,包括 Linux 容器、Linux 上的 Azure 应用服务、AWS Lambda 和本地 Windows 服务器。 IronXL系统要求页面列出了完整的兼容性矩阵。
错误处理和调试
Interop失败表现为COM异常(COMException),这些异常很难映射到用户可见的信息。 已安装的 Office 版本与 Interop 程序集之间的版本不匹配会导致另一种故障模式。 调试这些问题通常需要在开发机器上重现完全相同的 Office 版本。
IronXL抛出具有描述性信息的标准System.Exception子类。 您可以将文件操作封装在 try-catch 块中,并在不了解 COM 错误代码的情况下提供有意义的反馈。 IronXL故障排除指南涵盖了常见异常及其解决方法。
内存和性能
COM 对象持有非托管内存。 如果你的代码处理很多工作表或在循环中运行,未能正确释放每个引用会导致内存随着时间的推移而增长——这个问题很难被发现,直到它导致生产事故。 由于 Interop 只驱动一个 Excel 窗口,因此它本质上也是单线程的。
IronXL使用由.NET垃圾回收器支持的托管对象。 当对象超出作用域时,内存会自动回收。 并行处理多个工作簿非常简单,因为没有需要用锁来保护的共享 COM 状态。
如何选择这两种方法?
选择合适的工具取决于两个限制条件:代码运行的位置,以及是否需要原生透视表 XML。
在以下情况下选择Excel互操作性:
- 该工作负载仅在已安装 Office 的 Windows 桌面计算机上运行。
- 输出文件必须完全符合原始数据透视表格式(切片器、分组日期字段、计算项)。
- 您维护着现有的互操作代码库,因此没有必要进行完全重写。
在以下情况下选择IronXL:
- 该应用程序运行在服务器、容器或云函数上
- 需要跨平台或Linux部署
- 您需要的是一个可使用单元测试进行测试的数据聚合逻辑,而不是依赖于 Excel 流程的逻辑。
- 您需要大规模或并行处理 Excel 文件。
对于大多数新的.NET 10 项目而言, IronXL是实际的默认选择。 IronXL库还涵盖在C#中读取Excel文件、创建图表、应用单元格样式、公式操作和将Excel数据导出到DataTable——减少了在您的堆栈中需要多库的需求。

IronXL还有哪些功能支持数据分析?
除了基本的聚合功能外, IronXL还提供了多种功能,支持更丰富的数据分析工作流程:
数据排序和筛选
在编写汇总表之前,您可以按升序或降序对数据范围进行排序。 这样可以确保输出始终以一致的顺序呈现数据,从而使下游处理更具可预测性。 请参见IronXL排序文档,了解范围级别的排序选项。
应用条件格式
通过条件格式规则应用突出显示异常值或阈值。 例如,当销售额低于目标时,您可以将汇总表中的单元格颜色设置为红色,从而提供一目了然的视图,而无需最终用户手动配置规则。
读取现有 Excel 文件
如果源数据来自现有电子表格而不是数据库,可以使用WorkBook.Load直接加载。
using IronXL;
WorkBook existing = WorkBook.Load(@"C:\data\sales_report.xlsx");
WorkSheet sheet = existing.WorkSheets[0];
// Read column C starting at row 2
var salesValues = sheet["C2:C100"]
.Where(cell => cell.Value != null && cell.Value.ToString() != string.Empty)
.Select(cell => cell.DecimalValue)
.ToList();
decimal total = salesValues.Sum();
Console.WriteLine($"Total sales from file: {total:C}");Imports IronXL
Dim existing As WorkBook = WorkBook.Load("C:\data\sales_report.xlsx")
Dim sheet As WorkSheet = existing.WorkSheets(0)
' Read column C starting at row 2
Dim salesValues = sheet("C2:C100") _
.Where(Function(cell) cell.Value IsNot Nothing AndAlso cell.Value.ToString() <> String.Empty) _
.Select(Function(cell) cell.DecimalValue) _
.ToList()
Dim total As Decimal = salesValues.Sum()
Console.WriteLine($"Total sales from file: {total:C}")此模式适用于.csv,以及IronXL支持的其他格式。 有关支持的格式的详细信息,请参阅IronXL文件格式文档。
导出为 CSV
生成汇总工作表后,您可以将其导出为 CSV 文件,以便供不支持 Excel 的下游系统使用:
workbook.SaveAsCsv(@"C:\output\summary.csv");workbook.SaveAsCsv("C:\output\summary.csv")这在 ETL 管道中尤其有用,因为最终使用者是数据库导入工具或数据仓库加载器。
如何免费开始使用IronXL ?
IronXL提供免费试用许可,让您无需订阅即可评估所有功能。 试用版会在输出文件上添加水印,应用正式版许可证密钥后即可移除水印。
要在代码中激活许可证密钥,请在执行任何IronXL操作之前进行设置:
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE";Imports IronXL
IronXL.License.LicenseKey = "YOUR-LICENSE-KEY-HERE"您也可以通过环境变量(IRONXL_LICENSEKEY)或通过在ASP.NET Core项目中使用appsettings.json设置密钥。 IronXL许可页面描述了所有可用的级别,包括团队和组织选项。
对于生产部署,请查看IronXL部署文档和更广泛的IronXL教程库,涵盖ASP.NET Core、Azure Functions和Docker的模式。
如何迈出下一步?
C# 中的 Excel 数据透视表并不一定意味着对 Office 的依赖和 COM 的复杂性。 使用IronXL,您可以编写可在任何平台上运行的纯.NET代码,自动处理内存,并自然地与单元测试框架和 CI/CD 管道集成。
下载免费的IronXL试用版,并使用本指南中的示例对您自己的数据运行该示例。 如果您遇到任何问题, IronXL社区论坛和支持门户网站对试用用户和授权用户均开放。 准备部署时,请查看许可选项,找到适合您团队规模和使用量的方案。

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



