跳至页脚内容
使用 IRONXL

如何在不依赖 Office 的情况下使用 C# 在 Excel 中创建透视表?

传统上,在 C# 中以编程方式创建数据透视表需要 Office Interop 和复杂的 COM 管理,但像IronXL这样的现代库无需安装 Office 即可生成跨平台数据透视表,支持 Docker 容器和云部署,同时消除内存泄漏。

以编程方式生成数据透视表需要使用 C# Interop 及其 Office 依赖项,或者使用像IronXL这样可以独立工作的现代库。 本教程演示了这两种方法,重点介绍了为什么开发人员越来越多地选择IronXL 的容器友好型解决方案而不是传统方法。

在本文中,我们将学习如何编辑、创建、设计和计算具有自动分析和错误处理功能的透视表。 无论您是部署到 AWS还是在 Azure 上运行,本指南都涵盖了 Excel 自动化的现代方法。

什么是Excel数据透视表?

为什么数据透视表对数据分析很重要?

数据透视表是 Excel 中最强大的工具之一,可用于汇总大型数据集。 它提供了一种简便的方法来显示、理解和分析数值数据。 数据透视表不仅在 Excel 中可用,而且在 Google Sheets、Apple Numbers 和 CSV 导出中也可用。 它们通过创建与您的底层信息相链接的交互式摘要,将原始数据转化为有意义的见解。

对于在 C# 中使用 Excel 公式的开发人员来说,数据透视表代表了至关重要的聚合功能。 与只能对单个单元格进行运算的基本数学函数不同,数据透视表可以对整个数据集进行 Excel 函数聚合

何时应该使用数据透视表,何时应该使用常规报表?

让我们先来探讨一下创建数据透视表的错误方法,然后再学习在 C# 中创建数据透视表的正确方法:

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

尽管Interop存在诸多局限性,但为什么它仍然被使用?

C# Excel Interop 通过 COM 自动化提供对 Excel 数据透视表功能的直接访问。 以下是许多开发人员在 C# 中搜索数据透视表生成方法时遇到的传统方法:(已弃用)

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

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

// Create Excel application instance - requires Office installation
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 - COM objects require explicit cleanup
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 - traditional row/column/data setup
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 - critical for preventing memory leaks
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();

// Release COM objects to prevent memory leaks - must release in reverse order
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 - requires Office installation
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 - COM objects require explicit cleanup
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 - traditional row/column/data setup
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 - critical for preventing memory leaks
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();

// Release COM objects to prevent memory leaks - must release in reverse order
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 - requires Office installation
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 - COM objects require explicit cleanup
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 - traditional row/column/data setup
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 - critical for preventing memory leaks
xlWorkbook.SaveAs("C:\Data\PivotReport.xlsx")
xlWorkbook.Close()
xlApp.Quit()

' Release COM objects to prevent memory leaks - must release in reverse order
Marshal.ReleaseComObject(pivotTable)
Marshal.ReleaseComObject(pivotCache)
Marshal.ReleaseComObject(xlPivotSheet)
Marshal.ReleaseComObject(xlSheet)
Marshal.ReleaseComObject(xlWorkbook)
Marshal.ReleaseComObject(xlApp)
$vbLabelText   $csharpLabel

此 Interop 示例创建了一个原生 Excel 数据透视表,其中产品为行,地区为列,销售额汇总在数据区域中。 虽然功能正常,但这种方法需要安装Microsoft Office并仔细管理COM对象。 微软自己的文档现在也不鼓励在现代开发中使用这种方法。 对于容器化部署,在不使用 Interop 的情况下使用 Excel至关重要。

如果 COM 对象没有正确释放会发生什么情况?

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

为什么在容器化环境中互操作会失败?

互操作性方法给现代DevOps实践和Docker 设置带来了几个重大挑战:

部署依赖项:需要在运行源代码的每台机器上安装 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; // Essential for DataTable manipulation
using static System.Data.DataTableExtensions; // Extension methods for LINQ queries

class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - works on all platforms without Office
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];

        // Convert to DataTable for powerful manipulation - maintains data types
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers

        // Create pivot-style aggregation using LINQ - no COM objects needed
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) // Group by multiple dimensions
            .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 - no Excel process started
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");

        // Build cross-tabulation structure programmatically
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);

        // Create headers with formatting options
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // Dynamic column addressing
            col++;
        }

        // Populate pivot data - memory efficient for large datasets
        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 - maintains live calculations
        pivotSheet[$"A{row}"].Value = "Total"; // Grand totals row
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            // Formula references ensure dynamic updates
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = 
                $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }

        // Apply professional formatting - currency format for sales data
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";

        // Save without Office dependencies - works in containers
        workbook.SaveAs("PivotReport.xlsx");
    }
}
using IronXL;
using System.Linq;
using System.Data; // Essential for DataTable manipulation
using static System.Data.DataTableExtensions; // Extension methods for LINQ queries

class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - works on all platforms without Office
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];

        // Convert to DataTable for powerful manipulation - maintains data types
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers

        // Create pivot-style aggregation using LINQ - no COM objects needed
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) // Group by multiple dimensions
            .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 - no Excel process started
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");

        // Build cross-tabulation structure programmatically
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);

        // Create headers with formatting options
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // Dynamic column addressing
            col++;
        }

        // Populate pivot data - memory efficient for large datasets
        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 - maintains live calculations
        pivotSheet[$"A{row}"].Value = "Total"; // Grand totals row
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            // Formula references ensure dynamic updates
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = 
                $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }

        // Apply professional formatting - currency format for sales data
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";

        // Save without Office dependencies - works in containers
        workbook.SaveAs("PivotReport.xlsx");
    }
}
Imports IronXL
Imports System.Linq
Imports System.Data ' Essential for DataTable manipulation
Imports System.Data.DataTableExtensions ' Extension methods for LINQ queries

Module Program
    Sub Main(args As String())
        ' Load Excel file - works on all platforms without Office
        Dim workbook As WorkBook = WorkBook.Load("SalesData.xlsx")
        Dim dataSheet As WorkSheet = workbook.WorkSheets(0)

        ' Convert to DataTable for powerful manipulation - maintains data types
        Dim dataTable = dataSheet.ToDataTable(True) ' True = use first row as column headers

        ' Create pivot-style aggregation using LINQ - no COM objects needed
        Dim pivotData = dataTable.AsEnumerable() _
            .GroupBy(Function(row) New With {
                Key .Product = row("Product").ToString(),
                Key .Region = row("Region").ToString()
            }) _
            .Select(Function(g) New With {
                .Product = g.Key.Product,
                .Region = g.Key.Region,
                .TotalSales = g.Sum(Function(row) Convert.ToDecimal(row("Sales"))),
                .AverageSale = g.Average(Function(row) Convert.ToDecimal(row("Sales"))),
                .Count = g.Count()
            })

        ' Create pivot report worksheet - no Excel process started
        Dim pivotSheet As WorkSheet = workbook.CreateWorkSheet("PivotReport")

        ' Build cross-tabulation structure programmatically
        Dim products = pivotData.Select(Function(p) p.Product).Distinct().OrderBy(Function(p) p)
        Dim regions = pivotData.Select(Function(p) p.Region).Distinct().OrderBy(Function(r) r)

        ' Create headers with formatting options
        pivotSheet("A1").Value = "Product/Region"
        Dim col As Integer = 2
        For Each region In regions
            pivotSheet($"{ChrW(Asc("A"c) + col - 1)}1").Value = region ' Dynamic column addressing
            col += 1
        Next

        ' Populate pivot data - memory efficient for large datasets
        Dim row As Integer = 2
        For Each product In products
            pivotSheet($"A{row}").Value = product
            col = 2
            For Each region In regions
                Dim sales = pivotData _
                    .Where(Function(p) p.Product = product AndAlso p.Region = region) _
                    .Select(Function(p) p.TotalSales) _
                    .FirstOrDefault()
                pivotSheet($"{ChrW(Asc("A"c) + col - 1)}{row}").Value = sales
                col += 1
            Next
            row += 1
        Next

        ' Add totals using Excel formulas - maintains live calculations
        pivotSheet($"A{row}").Value = "Total" ' Grand totals row
        For c As Integer = 2 To regions.Count() + 1
            ' Formula references ensure dynamic updates
            pivotSheet($"{ChrW(Asc("A"c) + c - 1)}{row}").Formula = 
                $"=SUM({ChrW(Asc("A"c) + c - 1)}2:{ChrW(Asc("A"c) + c - 1)}{row - 1})"
        Next

        ' Apply professional formatting - currency format for sales data
        Dim dataRange = pivotSheet($"B2:{ChrW(Asc("A"c) + regions.Count())}{row}")
        dataRange.FormatString = "$#,##0.00"

        ' Save without Office dependencies - works in containers
        workbook.SaveAs("PivotReport.xlsx")
    End Sub
End Module
$vbLabelText   $csharpLabel

这种现代方法创建的透视表可以在 Docker 容器中无缝运行,并支持各种 Excel 格式。 您还可以导出为不同的格式,包括CSVJSON 和 XML

输出结果是什么样子的?

左侧是显示原始销售数据的 Excel 表格,右侧是按地区汇总产品销售情况的数据透视表。

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

什么时候应该使用公式而不是静态聚合?

对于需要类似于数据透视表刷新功能的动态更新场景,IronXL 可以利用Excel 的内置公式。 这种方法更加优雅且易于维护,代码无需手册或支持即可轻松理解。 它与用于可视化数据呈现的条件格式配合使用效果很好。

基于公式的汇总如何维护数据连接?

// Load the workbook - container-friendly approach
WorkBook workbook = WorkBook.Load(inputPath);

// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet"; // Named reference for formulas

// Convert worksheet to DataTable for efficient processing
DataTable dataTable = dataSheet.ToDataTable(true);

// Create new summary worksheet - no COM objects
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");

// Get unique product-region combinations using LINQ
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 with proper formatting
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";

// Populate rows with formulas - maintains live data connection
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;

    // SUMIFS formula for conditional aggregation
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";

    // COUNTIFS for record counting
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";

    rowIndex++;
}

// Optional: add total row with grand totals
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";

// Apply number formatting for professional appearance
var salesColumn = summarySheet[$"C2:C{rowIndex}"];
salesColumn.FormatString = "$#,##0.00";

// Save output file - works in any environment
workbook.SaveAs(outputPath);  // No Office required
// Load the workbook - container-friendly approach
WorkBook workbook = WorkBook.Load(inputPath);

// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet"; // Named reference for formulas

// Convert worksheet to DataTable for efficient processing
DataTable dataTable = dataSheet.ToDataTable(true);

// Create new summary worksheet - no COM objects
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");

// Get unique product-region combinations using LINQ
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 with proper formatting
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";

// Populate rows with formulas - maintains live data connection
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;

    // SUMIFS formula for conditional aggregation
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";

    // COUNTIFS for record counting
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";

    rowIndex++;
}

// Optional: add total row with grand totals
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";

// Apply number formatting for professional appearance
var salesColumn = summarySheet[$"C2:C{rowIndex}"];
salesColumn.FormatString = "$#,##0.00";

// Save output file - works in any environment
workbook.SaveAs(outputPath);  // No Office required
Imports System.Data
Imports System.Linq

' Load the workbook - container-friendly approach
Dim workbook As WorkBook = WorkBook.Load(inputPath)

' Rename the first worksheet so formulas reference correctly
Dim dataSheet As WorkSheet = workbook.WorkSheets(0)
dataSheet.Name = "DataSheet" ' Named reference for formulas

' Convert worksheet to DataTable for efficient processing
Dim dataTable As DataTable = dataSheet.ToDataTable(True)

' Create new summary worksheet - no COM objects
Dim summarySheet As WorkSheet = workbook.CreateWorkSheet("DynamicSummary")

' Get unique product-region combinations using LINQ
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)

' Add header row with proper formatting
summarySheet("A1").Value = "Product"
summarySheet("B1").Value = "Region"
summarySheet("C1").Value = "Total Sales"
summarySheet("D1").Value = "Count"

' Populate rows with formulas - maintains live data connection
Dim rowIndex As Integer = 2
For Each combo In uniqueCombos
    summarySheet($"A{rowIndex}").Value = combo.Product
    summarySheet($"B{rowIndex}").Value = combo.Region

    ' SUMIFS formula for conditional aggregation
    summarySheet($"C{rowIndex}").Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,""{combo.Product}"",DataSheet!B:B,""{combo.Region}"")"

    ' COUNTIFS for record counting
    summarySheet($"D{rowIndex}").Formula =
        $"=COUNTIFS(DataSheet!A:A,""{combo.Product}"",DataSheet!B:B,""{combo.Region}"")"

    rowIndex += 1
Next

' Optional: add total row with grand totals
summarySheet($"A{rowIndex}").Value = "Total"
summarySheet($"C{rowIndex}").Formula = $"=SUM(C2:C{rowIndex - 1})"
summarySheet($"D{rowIndex}").Formula = $"=SUM(D2:D{rowIndex - 1})"

' Apply number formatting for professional appearance
Dim salesColumn = summarySheet($"C2:C{rowIndex}")
salesColumn.FormatString = "$#,##0.00"

' Save output file - works in any environment
workbook.SaveAs(outputPath) ' No Office required
$vbLabelText   $csharpLabel

这些公式与源数据保持实时连接,当数据表更改时自动更新——类似于数据透视表刷新行为,但没有互操作依赖性。 对于复杂的场景,您可以 创建 Excel 图表使用命名区域来更好地管理公式。

基于公式的方法能带来哪些性能优势?

将此代码应用于我们的示例 Excel 文件,将产生以下输出:

Excel电子表格显示了产品销售数据,包含产品、地区、总销售额和数量等列,展示了不同地区各种电子产品(笔记本电脑、手机、平板电脑)的销售情况,其中部分地区的销售额为零。

基于公式的方法具有显著的性能优势:它们在 Excel 的计算引擎中原生执行,支持后台计算,并且可以与Excel 的打印设置无缝集成以生成报表。 您还可以应用单元格格式样式来提高可读性。

C# Interop 与 IronXL 在数据透视表方面有何不同?

哪些部署场景更适合采用哪种方法?

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

每种解决方案需要哪些资源?

C# 互操作需要大量的系统资源:完整的 Office 安装(2-4GB 磁盘空间)、Windows 操作系统、足够的内存以运行 Excel 进程,以及 COM 注册的管理权限。 相比之下,IronXL 只需要 .NET 运行时和大约 50MB 的库,因此非常适合资源受限的环境。 IronXL 的文件大小限制有详细的文档说明,可用于容量规划。

你应该选择哪种方法?

互操作性何时仍然有意义?

选择 C# 互操作的条件:

  • 必须使用原生 Excel 数据透视表对象
  • 仅在安装了 Office 的 Windows 系统上工作
  • 仅部署到您管理的桌面系统
  • 现有遗留代码依赖于互操作性
  • 使用旧版 .NET Framework
  • 需要使用其他 Excel 功能无法实现的特定功能。

针对这些有限的场景,请确保正确的错误处理和 COM 清理模式。

为什么DevOps团队更喜欢 IronXL?

选择 IronXL 的情况:

  • 部署到服务器或云环境(Azure、AWS) 构建跨平台应用程序
  • 要求更高的性能和可靠性
  • 避免办公软件许可费用 需要更简洁、更易于维护的代码
  • 支持 Mac、iOS、Android 或 Linux 系统
  • 使用现代 .NET Core 和 .NET 5+
  • 希望能够完全通过程序控制数据透视表的配置
  • 构建Blazor 应用程序 创建从 SQL 数据库加载 Excel 的微服务

IronXL 还提供广泛的安全功能,包括工作簿加密工作表保护

现代发展的最佳路径是什么?

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

对于寻求无需 Interop 即可进行数据透视表开发的开发人员来说,IronXL 提供了一条更优的途径,避免了 COM 的复杂性,可在所有平台上运行,并简化了部署。 虽然没有本机数据透视对象的权衡通过更大的灵活性、更好的性能和消除Office许可要求得以补偿。 您可以创建电子表格加载现有文件,甚至可以在需要时使用 VB.NET

现代DevOps实践需要对容器友好的解决方案。 IronXL 通过全面的文档丰富的示例定期更新来满足不断变化的部署需求。

立即开始使用 IronXL。
green arrow pointer

准备好让您的 Excel 自动化流程现代化,并使用现代 C# 创建您自己的数据透视表代码了吗?

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

常见问题解答

使用 IronXL 在 Excel 中创建数据透视表的优势是什么?

IronXL 可让您在 Excel 中创建数据透视表,而无需依赖 Office,与传统的 C# 互操作方法相比,它是一种更精简、更高效的解决方案。

IronXL 如何处理数据透视表的数据操作?

IronXL.Excel 提供强大的数据操作功能,能够创建透视风格的报表,而不会出现 Excel Interop 所带来的复杂问题。

IronXL 能否独立于 Excel Interop 使用?

是的,IronXL.Excel 可独立工作,允许开发人员生成数据透视表,而无需依赖 Excel Interop 及其相关依赖项。

与传统的 Excel 互操作方法相比,为什么开发人员更青睐 IronXL.Excel?

开发人员之所以偏爱 IronXL,是因为它省去了传统 Interop 方法所需的 Office 依赖关系,从而简化了创建数据透视表的过程。

使用 IronXL 需要安装 Microsoft Office 吗?

不,IronXL 不需要安装 Microsoft Office,因为它独立于 Office 运行,不像 C# Interop 需要依赖 Office。

IronXL 兼容现代 C# 编程吗?

是的,IronXL 的设计与现代 C# 编程无缝集成,提供了一种处理 Excel 数据操作任务的现代方法。

Curtis Chau
技术作家

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

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