.NET驾驭Excel之力:Excel应用程序的创建与管理

本文将深入探讨MudTools.OfficeInterop.Excel如何创建和管理Excel应用程序实例。Excel应用程序是整个Excel自动化的核心,正确管理应用程序实例对于确保程序的稳定性、性能和资源释放至关重要。

目标

  • 掌握ExcelFactory类的各种创建方法
  • 理解应用程序生命周期管理
  • 学习异常处理和资源释放策略
  • 掌握多实例管理的最佳实践

关键概念

  • Excel应用程序实例:代表Excel程序的运行实例
  • 工作簿:Excel文件的基本容器
  • 工作表:工作簿中的具体数据表
  • COM对象:Excel与.NET通信的底层技术

2.1 ExcelFactory类详解

ExcelFactory类是MudTools.OfficeInterop.Excel库的核心类,提供了多种创建Excel应用程序实例的方法。每种方法都有其特定的应用场景和优缺点。

2.1.1 BlankWorkbook方法

BlankWorkbook()方法是最常用的创建方式,它会启动一个Excel应用程序并创建一个空白工作簿。

csharp 复制代码
/// <summary>
/// 创建空白工作簿示例
/// 演示如何使用ExcelFactory.BlankWorkbook()方法创建Excel应用程序实例
/// </summary>
static void BlankWorkbookExample()
{
    Console.WriteLine("=== 创建空白工作簿示例 ===");

    try
    {
        // 使用BlankWorkbook方法创建Excel应用程序实例
        using var excelApp = ExcelFactory.BlankWorkbook();

        // 获取活动工作簿
        var workbook = excelApp.ActiveWorkbook;

        // 获取活动工作表
        var worksheet = workbook.ActiveSheetWrap;

        // 设置工作表名称
        worksheet.Name = "空白工作簿示例";

        // 添加标题
        worksheet.Range("A1").Value = "Excel应用程序管理 - 空白工作簿示例";
        worksheet.Range("A1").Font.Bold = true;
        worksheet.Range("A1").Font.Size = 16;
        worksheet.Range("A1").Interior.Color = System.Drawing.Color.LightBlue;

        // 保存工作簿
        string fileName = $"BlankWorkbookExample_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        workbook.SaveAs(fileName);

        Console.WriteLine($"✓ 成功创建空白工作簿: {fileName}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"✗ 创建空白工作簿时出错: {ex.Message}");
    }
}

方法特点:

  • ✅ 自动创建新工作簿
  • ✅ 适用于大多数简单场景
  • ✅ 代码简洁易用
  • ❌ 无法指定特定Excel版本

2.1.2 CreateInstance方法

CreateInstance(string progId)方法允许通过指定ProgID来创建特定版本的Excel实例。

csharp 复制代码
/// <summary>
/// 通过ProgID创建特定版本实例示例
/// 演示如何使用ExcelFactory.CreateInstance()方法创建特定版本的Excel应用程序实例
/// </summary>
static void CreateInstanceExample()
{
    Console.WriteLine("=== 通过ProgID创建特定版本实例示例 ===");

    try
    {
        // 使用CreateInstance方法创建特定版本的Excel应用程序实例
        // Excel.Application是Excel的ProgID
        using var excelApp = ExcelFactory.CreateInstance("Excel.Application");

        // 设置Excel应用程序可见性
        excelApp.Visible = true;

        // 创建新工作簿
        var workbook = excelApp.BlankWorkbook();

        // 获取活动工作表
        var worksheet = workbook.ActiveSheetWrap;

        // 设置工作表名称
        worksheet.Name = "特定版本示例";

        // 添加示例数据
        worksheet.Range("A5").Value = "Excel版本: " + excelApp.Version;

        string fileName = $"CreateInstanceExample_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        workbook.SaveAs(fileName);

        Console.WriteLine($"✓ 成功创建特定版本实例工作簿: {fileName}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"✗ 通过ProgID创建特定版本实例时出错: {ex.Message}");
    }
}

支持的ProgID示例:

  • "Excel.Application" - 最新版本的Excel
  • "Excel.Application.16" - Excel 2016
  • "Excel.Application.15" - Excel 2013
  • "Excel.Application.14" - Excel 2010

2.1.3 CreateFrom方法

CreateFrom(string templatePath)方法基于现有模板创建新的工作簿。

csharp 复制代码
/// <summary>
/// 基于模板创建工作簿示例
/// 演示如何使用ExcelFactory.CreateFrom()方法基于模板创建工作簿
/// </summary>
static void CreateFromTemplateExample()
{
    Console.WriteLine("=== 基于模板创建工作簿示例 ===");

    try
    {
        // 首先创建一个模板文件
        CreateTemplateFile();

        // 使用CreateFrom方法基于模板创建工作簿
        using var excelApp = ExcelFactory.CreateFrom("Template.xlsx");

        // 获取活动工作簿
        var workbook = excelApp.ActiveWorkbook;

        // 获取活动工作表
        var worksheet = workbook.ActiveSheetWrap;

        // 填充模板中的数据
        worksheet.Range("B2").Value = "张三";
        worksheet.Range("B3").Value = "Excel自动化工程师";
        worksheet.Range("B4").Value = DateTime.Now.ToString("yyyy-MM-dd");
        worksheet.Range("B5").Value = "北京市朝阳区";

        string fileName = $"CreateFromTemplateExample_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        workbook.SaveAs(fileName);

        Console.WriteLine($"✓ 成功基于模板创建工作簿: {fileName}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"✗ 基于模板创建工作簿时出错: {ex.Message}");
    }
}

模板创建示例:

csharp 复制代码
static void CreateTemplateFile()
{
    try
    {
        // 创建Excel应用程序实例
        using var excelApp = ExcelFactory.BlankWorkbook();

        // 获取活动工作簿和工作表
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;

        // 设置模板内容
        worksheet.Range("A1").Value = "员工信息表";
        worksheet.Range("A1").Font.Bold = true;
        worksheet.Range("A1").Font.Size = 16;
        worksheet.Range("A1").Interior.Color = System.Drawing.Color.LightYellow;

        worksheet.Range("A2").Value = "姓名:";
        worksheet.Range("A3").Value = "职位:";
        worksheet.Range("A4").Value = "入职日期:";
        worksheet.Range("A5").Value = "地址:";

        // 添加边框
        worksheet.Range("A1:B6").Borders.LineStyle = XlLineStyle.xlContinuous;

        // 保存为模板文件
        workbook.SaveAs("Template.xlsx");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"创建模板文件时出错: {ex.Message}");
    }
}

2.1.4 Open方法

Open(string filePath)方法用于打开现有的Excel文件。

csharp 复制代码
/// <summary>
/// 打开现有工作簿示例
/// 演示如何使用ExcelFactory.Open()方法打开现有工作簿
/// </summary>
static void OpenExistingWorkbookExample()
{
    Console.WriteLine("=== 打开现有工作簿示例 ===");

    try
    {
        // 首先创建一个要打开的文件
        CreateExistingFile();

        // 使用Open方法打开现有工作簿
        using var excelApp = ExcelFactory.Open("ExistingFile.xlsx");

        // 获取活动工作簿
        var workbook = excelApp.ActiveWorkbook;

        // 获取活动工作表
        var worksheet = workbook.ActiveSheetWrap;

        // 读取现有数据
        var existingValue = worksheet.Range("A1").Value;
        Console.WriteLine($"原始数据: {existingValue}");

        // 修改数据
        worksheet.Range("A3").Value = "这是通过ExcelFactory.Open()方法打开并修改的数据";
        worksheet.Range("A4").Value = "修改时间: " + DateTime.Now.ToString();

        string fileName = $"OpenExistingWorkbookExample_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        workbook.SaveAs(fileName);

        Console.WriteLine($"✓ 成功打开并修改现有工作簿: {fileName}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"✗ 打开现有工作簿时出错: {ex.Message}");
    }
}

2.2 应用程序生命周期管理

正确的生命周期管理是Excel自动化开发的关键,不当的管理会导致资源泄漏和程序不稳定。

2.2.1 使用using语句进行资源管理

csharp 复制代码
// 推荐的使用方式
using var excelApp = ExcelFactory.BlankWorkbook();
var workbook = excelApp.ActiveWorkbook;

// 执行Excel操作
workbook.ActiveSheetWrap.Range("A1").Value = "Hello World";

// 自动释放资源 - using语句结束时

2.2.2 手动资源释放模式

csharp 复制代码
// 手动资源管理示例
ExcelApplicationWrapper excelApp = null;
try
{
    excelApp = ExcelFactory.BlankWorkbook();
    
    // 执行Excel操作
    var workbook = excelApp.ActiveWorkbook;
    workbook.ActiveSheetWrap.Range("A1").Value = "Hello World";
    
    // 保存文件
    workbook.SaveAs("manual_management.xlsx");
}
catch (Exception ex)
{
    Console.WriteLine($"操作失败: {ex.Message}");
}
finally
{
    // 手动释放资源
    excelApp?.Dispose();
}

2.2.3 异常处理策略

csharp 复制代码
/// <summary>
/// 完整的异常处理示例
/// </summary>
static void SafeExcelOperation()
{
    ExcelApplicationWrapper excelApp = null;
    try
    {
        // 创建Excel实例
        excelApp = ExcelFactory.BlankWorkbook();
        
        // 设置应用程序属性
        excelApp.Visible = false; // 后台运行
        excelApp.DisplayAlerts = false; // 禁用警告
        
        // 获取工作簿
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;
        
        // 执行复杂操作
        for (int i = 1; i <= 100; i++)
        {
            worksheet.Range($"A{i}").Value = $"数据行 {i}";
            
            // 添加进度检查点
            if (i % 10 == 0)
            {
                Console.WriteLine($"已处理 {i} 行数据");
            }
        }
        
        // 保存结果
        workbook.SaveAs("safe_operation_result.xlsx");
        Console.WriteLine("✓ 操作成功完成");
    }
    catch (COMException comEx)
    {
        Console.WriteLine($"COM异常: {comEx.Message}");
        Console.WriteLine($"错误代码: {comEx.ErrorCode}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"一般异常: {ex.Message}");
    }
    finally
    {
        // 确保资源释放
        try
        {
            excelApp?.Quit();
            excelApp?.Dispose();
        }
        catch (Exception disposeEx)
        {
            Console.WriteLine($"资源释放异常: {disposeEx.Message}");
        }
    }
}

2.3 应用程序属性配置

Excel应用程序提供了丰富的属性来控制其行为,合理配置这些属性可以显著提升程序性能和用户体验。

2.3.1 可见性控制

csharp 复制代码
/// <summary>
/// 可见性控制示例
/// </summary>
static void VisibilityControlExample()
{
    // 前台模式 - 用户可见
    using var visibleApp = ExcelFactory.BlankWorkbook();
    visibleApp.Visible = true; // 显示Excel窗口
    visibleApp.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "前台模式";
    
    // 后台模式 - 无界面运行
    using var invisibleApp = ExcelFactory.BlankWorkbook();
    invisibleApp.Visible = false; // 隐藏Excel窗口
    invisibleApp.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "后台模式";
    
    Console.WriteLine("两种模式都已完成");
}

2.3.2 警告和提示控制

csharp 复制代码
/// <summary>
/// 警告控制示例
/// </summary>
static void AlertControlExample()
{
    using var excelApp = ExcelFactory.BlankWorkbook();
    
    // 禁用警告(适合批量操作)
    excelApp.DisplayAlerts = false;
    
    // 启用警告(适合交互式操作)
    // excelApp.DisplayAlerts = true;
    
    var workbook = excelApp.ActiveWorkbook;
    var worksheet = workbook.ActiveSheetWrap;
    
    // 尝试覆盖现有文件(在禁用警告时不会弹出确认对话框)
    worksheet.Range("A1").Value = "测试数据";
    workbook.SaveAs("test_file.xlsx");
    
    Console.WriteLine("文件已保存(禁用警告模式)");
}

2.3.3 屏幕更新控制

csharp 复制代码
/// <summary>
/// 屏幕更新控制示例(性能优化)
/// </summary>
static void ScreenUpdateControlExample()
{
    using var excelApp = ExcelFactory.BlankWorkbook();
    var workbook = excelApp.ActiveWorkbook;
    var worksheet = workbook.ActiveSheetWrap;
    
    // 禁用屏幕更新(大幅提升性能)
    excelApp.ScreenUpdating = false;
    
    try
    {
        // 执行大量数据填充操作
        for (int row = 1; row <= 1000; row++)
        {
            for (int col = 1; col <= 10; col++)
            {
                worksheet.Cells[row, col].Value = $"数据 {row}-{col}";
            }
        }
        
        Console.WriteLine("数据填充完成");
    }
    finally
    {
        // 恢复屏幕更新
        excelApp.ScreenUpdating = true;
    }
    
    workbook.SaveAs("large_data_file.xlsx");
    Console.WriteLine("文件已保存");
}

2.4 多实例管理

在企业级应用中,经常需要同时管理多个Excel实例,正确的多实例管理策略至关重要。

2.4.1 独立实例管理

csharp 复制代码
/// <summary>
/// 多实例独立管理示例
/// </summary>
static void MultiInstanceManagement()
{
    // 实例1:处理销售数据
    using var salesApp = ExcelFactory.BlankWorkbook();
    var salesWorkbook = salesApp.ActiveWorkbook;
    var salesSheet = salesWorkbook.ActiveSheetWrap;
    
    salesSheet.Name = "销售数据";
    salesSheet.Range("A1").Value = "销售报表";
    
    // 实例2:处理库存数据
    using var inventoryApp = ExcelFactory.BlankWorkbook();
    var inventoryWorkbook = inventoryApp.ActiveWorkbook;
    var inventorySheet = inventoryWorkbook.ActiveSheetWrap;
    
    inventorySheet.Name = "库存数据";
    inventorySheet.Range("A1").Value = "库存报表";
    
    // 同时操作两个实例
    salesSheet.Range("A2").Value = "销售数据正在处理...";
    inventorySheet.Range("A2").Value = "库存数据正在处理...";
    
    // 分别保存
    salesWorkbook.SaveAs("sales_report.xlsx");
    inventoryWorkbook.SaveAs("inventory_report.xlsx");
    
    Console.WriteLine("多实例操作完成");
}

2.4.2 实例池模式

csharp 复制代码
/// <summary>
/// Excel实例池管理器
/// </summary>
public class ExcelInstancePool : IDisposable
{
    private readonly Queue<ExcelApplicationWrapper> _pool = new();
    private readonly int _maxPoolSize;
    private readonly object _lockObject = new();
    
    public ExcelInstancePool(int maxPoolSize = 5)
    {
        _maxPoolSize = maxPoolSize;
    }
    
    /// <summary>
    /// 从池中获取Excel实例
    /// </summary>
    public ExcelApplicationWrapper GetInstance()
    {
        lock (_lockObject)
        {
            if (_pool.Count > 0)
            {
                return _pool.Dequeue();
            }
            
            if (_pool.Count < _maxPoolSize)
            {
                return CreateNewInstance();
            }
            
            throw new InvalidOperationException("实例池已满");
        }
    }
    
    /// <summary>
    /// 将实例返回到池中
    /// </summary>
    public void ReturnInstance(ExcelApplicationWrapper instance)
    {
        lock (_lockObject)
        {
            if (_pool.Count < _maxPoolSize)
            {
                _pool.Enqueue(instance);
            }
            else
            {
                instance.Dispose();
            }
        }
    }
    
    private ExcelApplicationWrapper CreateNewInstance()
    {
        var instance = ExcelFactory.BlankWorkbook();
        instance.Visible = false;
        instance.DisplayAlerts = false;
        instance.ScreenUpdating = false;
        return instance;
    }
    
    public void Dispose()
    {
        lock (_lockObject)
        {
            while (_pool.Count > 0)
            {
                _pool.Dequeue().Dispose();
            }
        }
    }
}

/// <summary>
/// 使用实例池的示例
/// </summary>
static void InstancePoolExample()
{
    using var pool = new ExcelInstancePool(3);
    
    // 并行处理多个任务
    var tasks = new List<Task>();
    
    for (int i = 1; i <= 5; i++)
    {
        int taskId = i;
        var task = Task.Run(() =>
        {
            var instance = pool.GetInstance();
            try
            {
                var workbook = instance.ActiveWorkbook;
                var worksheet = workbook.ActiveSheetWrap;
                
                worksheet.Range("A1").Value = $"任务 {taskId} 数据";
                worksheet.Range("A2").Value = $"处理时间: {DateTime.Now}";
                
                workbook.SaveAs($"task_{taskId}_result.xlsx");
                Console.WriteLine($"任务 {taskId} 完成");
            }
            finally
            {
                pool.ReturnInstance(instance);
            }
        });
        
        tasks.Add(task);
    }
    
    Task.WaitAll(tasks.ToArray());
    Console.WriteLine("所有并行任务完成");
}

2.5 高级应用场景

2.5.1 后台服务应用

csharp 复制代码
/// <summary>
/// 后台服务示例 - 定时处理Excel文件
/// </summary>
public class ExcelBackgroundService
{
    private readonly Timer _timer;
    private readonly string _watchDirectory;
    
    public ExcelBackgroundService(string watchDirectory, TimeSpan interval)
    {
        _watchDirectory = watchDirectory;
        _timer = new Timer(ProcessFiles, null, TimeSpan.Zero, interval);
    }
    
    private void ProcessFiles(object state)
    {
        try
        {
            var excelFiles = Directory.GetFiles(_watchDirectory, "*.xlsx");
            
            foreach (var filePath in excelFiles)
            {
                ProcessSingleFile(filePath);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"后台处理出错: {ex.Message}");
        }
    }
    
    private void ProcessSingleFile(string filePath)
    {
        using var excelApp = ExcelFactory.Open(filePath);
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;
        
        // 处理逻辑
        var data = worksheet.Range("A1").Value;
        Console.WriteLine($"处理文件: {Path.GetFileName(filePath)}, 数据: {data}");
        
        // 保存处理结果
        var resultPath = Path.Combine(
            Path.GetDirectoryName(filePath) ?? string.Empty,
            $"processed_{Path.GetFileName(filePath)}"
        );
        
        workbook.SaveAs(resultPath);
    }
    
    public void Stop()
    {
        _timer?.Dispose();
    }
}

// 使用示例
static void BackgroundServiceExample()
{
    var service = new ExcelBackgroundService(@"C:\ExcelFiles", TimeSpan.FromMinutes(5));
    
    Console.WriteLine("后台服务已启动,按任意键停止...");
    Console.ReadKey();
    
    service.Stop();
    Console.WriteLine("后台服务已停止");
}

2.5.2 模板应用系统

csharp 复制代码
/// <summary>
/// 模板应用系统
/// </summary>
public class TemplateApplicationSystem
{
    private readonly Dictionary<string, string> _templates;
    
    public TemplateApplicationSystem()
    {
        _templates = new Dictionary<string, string>
        {
            { "invoice", "Templates/invoice_template.xlsx" },
            { "report", "Templates/report_template.xlsx" },
            { "budget", "Templates/budget_template.xlsx" }
        };
    }
    
    /// <summary>
    /// 基于模板生成文档
    /// </summary>
    public string GenerateDocument(string templateType, Dictionary<string, object> data)
    {
        if (!_templates.ContainsKey(templateType))
            throw new ArgumentException($"模板类型 '{templateType}' 不存在");
        
        var templatePath = _templates[templateType];
        
        using var excelApp = ExcelFactory.CreateFrom(templatePath);
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;
        
        // 填充模板数据
        foreach (var kvp in data)
        {
            worksheet.Range(kvp.Key).Value = kvp.Value;
        }
        
        // 生成输出文件名
        var outputFileName = $"{templateType}_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        workbook.SaveAs(outputFileName);
        
        return outputFileName;
    }
}

// 使用示例
static void TemplateSystemExample()
{
    var system = new TemplateApplicationSystem();
    
    // 生成发票
    var invoiceData = new Dictionary<string, object>
    {
        { "B2", "INV-2023-001" },     // 发票编号
        { "B3", "张三" },              // 客户姓名
        { "B4", DateTime.Now.Date },   // 发票日期
        { "B5", 1500.50m },            // 金额
        { "B6", "增值税专用发票" }      // 发票类型
    };
    
    var invoiceFile = system.GenerateDocument("invoice", invoiceData);
    Console.WriteLine($"发票已生成: {invoiceFile}");
    
    // 生成报告
    var reportData = new Dictionary<string, object>
    {
        { "A1", "月度销售报告" },
        { "A2", DateTime.Now.ToString("yyyy年MM月") },
        { "B4", 250000m },             // 销售额
        { "B5", 15 },                   // 订单数量
        { "B6", 98.5m }                 // 完成率
    };
    
    var reportFile = system.GenerateDocument("report", reportData);
    Console.WriteLine($"报告已生成: {reportFile}");
}

2.5.3 文件版本控制系统

csharp 复制代码
/// <summary>
/// 文件版本控制系统
/// </summary>
public class FileVersionControlSystem
{
    private readonly string _baseDirectory;
    
    public FileVersionControlSystem(string baseDirectory)
    {
        _baseDirectory = baseDirectory;
        EnsureDirectoryExists(_baseDirectory);
    }
    
    /// <summary>
    /// 创建新版本
    /// </summary>
    public string CreateNewVersion(string originalFile, string versionComment)
    {
        if (!File.Exists(originalFile))
            throw new FileNotFoundException($"文件不存在: {originalFile}");
        
        var fileName = Path.GetFileNameWithoutExtension(originalFile);
        var extension = Path.GetExtension(originalFile);
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        
        var versionFileName = $"{fileName}_v{timestamp}{extension}";
        var versionFilePath = Path.Combine(_baseDirectory, versionFileName);
        
        // 复制文件并添加版本信息
        File.Copy(originalFile, versionFilePath, true);
        
        // 在版本文件中添加注释
        using var excelApp = ExcelFactory.Open(versionFilePath);
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;
        
        // 在最后一个单元格添加版本信息
        var lastCell = worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell);
        var versionRow = lastCell.Row + 2;
        
        worksheet.Cells[versionRow, 1].Value = $"版本创建时间: {DateTime.Now}";
        worksheet.Cells[versionRow + 1, 1].Value = $"版本注释: {versionComment}";
        
        workbook.Save();
        
        return versionFilePath;
    }
    
    /// <summary>
    /// 获取文件的所有版本
    /// </summary>
    public List<string> GetFileVersions(string fileName)
    {
        var searchPattern = $"{Path.GetFileNameWithoutExtension(fileName)}_v*{Path.GetExtension(fileName)}";
        return Directory.GetFiles(_baseDirectory, searchPattern)
                       .OrderByDescending(f => f)
                       .ToList();
    }
    
    /// <summary>
    /// 恢复到指定版本
    /// </summary>
    public void RestoreToVersion(string targetFile, string versionFile)
    {
        if (!File.Exists(versionFile))
            throw new FileNotFoundException($"版本文件不存在: {versionFile}");
        
        File.Copy(versionFile, targetFile, true);
        Console.WriteLine($"已恢复到版本: {Path.GetFileName(versionFile)}");
    }
    
    private void EnsureDirectoryExists(string directory)
    {
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
    }
}

// 使用示例
static void VersionControlExample()
{
    var versionSystem = new FileVersionControlSystem(@"C:\ExcelVersions");
    
    // 创建原始文件
    using var excelApp = ExcelFactory.BlankWorkbook();
    var workbook = excelApp.ActiveWorkbook;
    var worksheet = workbook.ActiveSheetWrap;
    
    worksheet.Range("A1").Value = "重要业务数据";
    worksheet.Range("A2").Value = "版本1.0";
    
    var originalFile = "business_data.xlsx";
    workbook.SaveAs(originalFile);
    
    // 创建多个版本
    var version1 = versionSystem.CreateNewVersion(originalFile, "初始版本");
    Console.WriteLine($"创建版本1: {Path.GetFileName(version1)}");
    
    // 修改文件
    worksheet.Range("A3").Value = "新增数据行";
    workbook.Save();
    
    var version2 = versionSystem.CreateNewVersion(originalFile, "添加新数据");
    Console.WriteLine($"创建版本2: {Path.GetFileName(version2)}");
    
    // 查看所有版本
    var versions = versionSystem.GetFileVersions(originalFile);
    Console.WriteLine("所有版本:");
    foreach (var version in versions)
    {
        Console.WriteLine($"  {Path.GetFileName(version)}");
    }
    
    // 恢复到第一个版本
    versionSystem.RestoreToVersion(originalFile, version1);
    Console.WriteLine("已恢复到初始版本");
}

2.6 性能优化和最佳实践

2.6.1 性能优化技巧

csharp 复制代码
/// <summary>
/// 高性能Excel操作示例
/// </summary>
static void HighPerformanceExample()
{
    using var excelApp = ExcelFactory.BlankWorkbook();
    
    // 1. 禁用不需要的功能
    excelApp.ScreenUpdating = false;
    excelApp.DisplayAlerts = false;
    excelApp.EnableEvents = false;
    excelApp.Calculation = XlCalculation.xlCalculationManual;
    
    var workbook = excelApp.ActiveWorkbook;
    var worksheet = workbook.ActiveSheetWrap;
    
    try
    {
        // 2. 批量操作数据(避免单个单元格操作)
        var dataArray = new object[1000, 10];
        for (int i = 0; i < 1000; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                dataArray[i, j] = $"数据{i + 1}-{j + 1}";
            }
        }
        
        // 一次性写入大量数据
        worksheet.Range("A1:J1000").Value = dataArray;
        
        // 3. 使用数组公式进行计算
        worksheet.Range("K1:K1000").FormulaArray = "=LEN(A1:J1)";
        
        Console.WriteLine("高性能操作完成");
    }
    finally
    {
        // 4. 恢复设置
        excelApp.Calculation = XlCalculation.xlCalculationAutomatic;
        excelApp.ScreenUpdating = true;
    }
    
    workbook.SaveAs("high_performance.xlsx");
}

2.6.2 内存管理最佳实践

csharp 复制代码
/// <summary>
/// 内存管理最佳实践示例
/// </summary>
static void MemoryManagementBestPractices()
{
    // 1. 及时释放COM对象引用
    ExcelApplicationWrapper excelApp = null;
    WorkbookWrapper workbook = null;
    
    try
    {
        excelApp = ExcelFactory.BlankWorkbook();
        workbook = excelApp.ActiveWorkbook;
        
        // 执行操作
        workbook.ActiveSheetWrap.Range("A1").Value = "测试数据";
        
        // 2. 显式释放不再使用的对象
        var tempRange = workbook.ActiveSheetWrap.Range("A1:B10");
        tempRange.Value = "临时数据";
        // tempRange不再需要时,可以显式释放
        System.Runtime.InteropServices.Marshal.ReleaseComObject(tempRange.ComObject);
        
        workbook.SaveAs("memory_managed.xlsx");
    }
    finally
    {
        // 3. 按照创建顺序的逆序释放资源
        workbook?.Close(false);
        excelApp?.Quit();
        
        // 4. 强制垃圾回收(谨慎使用)
        GC.Collect();
        GC.WaitForPendingFinalizers();
        
        workbook?.Dispose();
        excelApp?.Dispose();
    }
}

2.6.3 错误处理和恢复

csharp 复制代码
/// <summary>
/// 健壮的错误处理示例
/// </summary>
static void RobustErrorHandling()
{
    int retryCount = 0;
    const int maxRetries = 3;
    
    while (retryCount < maxRetries)
    {
        ExcelApplicationWrapper excelApp = null;
        
        try
        {
            excelApp = ExcelFactory.BlankWorkbook();
            
            // 模拟可能失败的操作
            if (DateTime.Now.Second % 3 == 0) // 模拟随机失败
            {
                throw new COMException("模拟COM操作失败");
            }
            
            // 正常操作
            excelApp.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "成功操作";
            excelApp.ActiveWorkbook.SaveAs("robust_operation.xlsx");
            
            Console.WriteLine("操作成功完成");
            break; // 成功,退出循环
        }
        catch (COMException comEx) when (retryCount < maxRetries - 1)
        {
            retryCount++;
            Console.WriteLine($"COM异常,第{retryCount}次重试: {comEx.Message}");
            
            // 等待一段时间后重试
            Thread.Sleep(1000 * retryCount);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"操作失败: {ex.Message}");
            break;
        }
        finally
        {
            excelApp?.Dispose();
        }
    }
    
    if (retryCount == maxRetries)
    {
        Console.WriteLine("操作失败,已达到最大重试次数");
    }
}

2.7 实际应用案例

2.7.1 多实例管理系统

csharp 复制代码
/// <summary>
/// 企业级多实例管理系统
/// </summary>
public class EnterpriseExcelInstanceManager
{
    private readonly ConcurrentDictionary<string, ExcelInstanceInfo> _instances;
    private readonly Timer _cleanupTimer;
    
    public EnterpriseExcelInstanceManager()
    {
        _instances = new ConcurrentDictionary<string, ExcelInstanceInfo>();
        _cleanupTimer = new Timer(CleanupInactiveInstances, null, 
            TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(5));
    }
    
    /// <summary>
    /// 获取或创建实例
    /// </summary>
    public ExcelApplicationWrapper GetInstance(string instanceKey, bool createIfNotExists = true)
    {
        if (_instances.TryGetValue(instanceKey, out var instanceInfo) && 
            instanceInfo.IsActive)
        {
            instanceInfo.LastAccessTime = DateTime.Now;
            return instanceInfo.Instance;
        }
        
        if (createIfNotExists)
        {
            var newInstance = ExcelFactory.BlankWorkbook();
            newInstance.Visible = false;
            
            var newInfo = new ExcelInstanceInfo
            {
                Instance = newInstance,
                LastAccessTime = DateTime.Now,
                IsActive = true
            };
            
            _instances[instanceKey] = newInfo;
            return newInstance;
        }
        
        throw new InvalidOperationException($"实例 '{instanceKey}' 不存在");
    }
    
    /// <summary>
    /// 释放实例
    /// </summary>
    public void ReleaseInstance(string instanceKey)
    {
        if (_instances.TryRemove(instanceKey, out var instanceInfo))
        {
            instanceInfo.Instance.Dispose();
        }
    }
    
    /// <summary>
    /// 清理不活跃的实例
    /// </summary>
    private void CleanupInactiveInstances(object state)
    {
        var cutoffTime = DateTime.Now.AddMinutes(-10); // 10分钟不活跃
        
        var inactiveKeys = _instances
            .Where(kvp => kvp.Value.LastAccessTime < cutoffTime)
            .Select(kvp => kvp.Key)
            .ToList();
        
        foreach (var key in inactiveKeys)
        {
            if (_instances.TryRemove(key, out var instanceInfo))
            {
                instanceInfo.Instance.Dispose();
                Console.WriteLine($"清理不活跃实例: {key}");
            }
        }
    }
    
    public void Dispose()
    {
        _cleanupTimer?.Dispose();
        
        foreach (var instanceInfo in _instances.Values)
        {
            instanceInfo.Instance.Dispose();
        }
        
        _instances.Clear();
    }
}

public class ExcelInstanceInfo
{
    public ExcelApplicationWrapper Instance { get; set; }
    public DateTime LastAccessTime { get; set; }
    public bool IsActive { get; set; }
}

// 使用示例
static void EnterpriseManagerExample()
{
    using var manager = new EnterpriseExcelInstanceManager();
    
    // 多个业务模块使用不同的实例
    var salesInstance = manager.GetInstance("SalesModule");
    var inventoryInstance = manager.GetInstance("InventoryModule");
    var financeInstance = manager.GetInstance("FinanceModule");
    
    // 各模块独立操作
    salesInstance.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "销售数据";
    inventoryInstance.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "库存数据";
    financeInstance.ActiveWorkbook.ActiveSheetWrap.Range("A1").Value = "财务数据";
    
    // 保存各自的文件
    salesInstance.ActiveWorkbook.SaveAs("sales_data.xlsx");
    inventoryInstance.ActiveWorkbook.SaveAs("inventory_data.xlsx");
    financeInstance.ActiveWorkbook.SaveAs("finance_data.xlsx");
    
    Console.WriteLine("企业级多实例管理完成");
}

2.7.2 后台服务应用

csharp 复制代码
/// <summary>
/// 高级后台服务 - 支持任务队列和优先级
/// </summary>
public class AdvancedExcelBackgroundService
{
    private readonly PriorityQueue<ExcelTask, int> _taskQueue;
    private readonly SemaphoreSlim _semaphore;
    private readonly CancellationTokenSource _cancellationTokenSource;
    private readonly List<Task> _workerTasks;
    
    public AdvancedExcelBackgroundService(int maxConcurrentTasks = 3)
    {
        _taskQueue = new PriorityQueue<ExcelTask, int>();
        _semaphore = new SemaphoreSlim(maxConcurrentTasks);
        _cancellationTokenSource = new CancellationTokenSource();
        _workerTasks = new List<Task>();
        
        // 启动工作线程
        for (int i = 0; i < maxConcurrentTasks; i++)
        {
            var workerTask = Task.Run(ProcessTasksAsync);
            _workerTasks.Add(workerTask);
        }
    }
    
    /// <summary>
    /// 添加处理任务
    /// </summary>
    public void AddTask(ExcelTask task, int priority = 0)
    {
        lock (_taskQueue)
        {
            _taskQueue.Enqueue(task, priority);
        }
    }
    
    private async Task ProcessTasksAsync()
    {
        while (!_cancellationTokenSource.Token.IsCancellationRequested)
        {
            await _semaphore.WaitAsync(_cancellationTokenSource.Token);
            
            ExcelTask task = null;
            lock (_taskQueue)
            {
                if (_taskQueue.TryDequeue(out task, out _))
                {
                    // 任务出队成功
                }
            }
            
            if (task != null)
            {
                try
                {
                    await ExecuteTaskAsync(task);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"任务执行失败: {ex.Message}");
                }
                finally
                {
                    _semaphore.Release();
                }
            }
            else
            {
                _semaphore.Release();
                await Task.Delay(1000); // 队列为空时等待
            }
        }
    }
    
    private async Task ExecuteTaskAsync(ExcelTask task)
    {
        using var excelApp = ExcelFactory.BlankWorkbook();
        excelApp.Visible = false;
        
        var workbook = excelApp.ActiveWorkbook;
        var worksheet = workbook.ActiveSheetWrap;
        
        // 执行任务特定的操作
        await task.ExecuteAsync(worksheet);
        
        // 保存结果
        workbook.SaveAs(task.OutputFileName);
        
        Console.WriteLine($"任务完成: {task.TaskName}");
    }
    
    public async Task StopAsync()
    {
        _cancellationTokenSource.Cancel();
        await Task.WhenAll(_workerTasks);
    }
}

public abstract class ExcelTask
{
    public string TaskName { get; set; }
    public string OutputFileName { get; set; }
    
    public abstract Task ExecuteAsync(WorksheetWrapper worksheet);
}

// 具体任务实现
public class DataProcessingTask : ExcelTask
{
    public List<string> Data { get; set; }
    
    public override async Task ExecuteAsync(WorksheetWrapper worksheet)
    {
        // 模拟数据处理
        for (int i = 0; i < Data.Count; i++)
        {
            worksheet.Cells[i + 1, 1].Value = Data[i];
            
            // 模拟耗时操作
            await Task.Delay(100);
        }
    }
}

// 使用示例
static async Task AdvancedBackgroundServiceExample()
{
    var service = new AdvancedExcelBackgroundService(2); // 最大并发2个任务
    
    // 添加高优先级任务
    var highPriorityTask = new DataProcessingTask
    {
        TaskName = "紧急数据处理",
        OutputFileName = "urgent_data.xlsx",
        Data = new List<string> { "紧急数据1", "紧急数据2", "紧急数据3" }
    };
    
    service.AddTask(highPriorityTask, 1); // 高优先级
    
    // 添加普通优先级任务
    for (int i = 1; i <= 5; i++)
    {
        var normalTask = new DataProcessingTask
        {
            TaskName = $"普通任务{i}",
            OutputFileName = $"normal_task_{i}.xlsx",
            Data = Enumerable.Range(1, 10).Select(n => $"数据{i}-{n}").ToList()
        };
        
        service.AddTask(normalTask, 0); // 普通优先级
    }
    
    // 等待一段时间让任务执行
    await Task.Delay(10000);
    
    // 停止服务
    await service.StopAsync();
    Console.WriteLine("高级后台服务已停止");
}

2.7.3 模板应用系统增强版

csharp 复制代码
/// <summary>
/// 增强版模板应用系统 - 支持动态模板和数据验证
/// </summary>
public class EnhancedTemplateApplicationSystem
{
    private readonly ITemplateRepository _templateRepository;
    private readonly IDataValidator _dataValidator;
    
    public EnhancedTemplateApplicationSystem(ITemplateRepository templateRepository, 
                                           IDataValidator dataValidator)
    {
        _templateRepository = templateRepository;
        _dataValidator = dataValidator;
    }
    
    /// <summary>
    /// 生成文档(带数据验证)
    /// </summary>
    public async Task<DocumentGenerationResult> GenerateDocumentAsync(
        string templateType, 
        Dictionary<string, object> data,
        DocumentGenerationOptions options = null)
    {
        options ??= new DocumentGenerationOptions();
        
        // 1. 数据验证
        var validationResult = await _dataValidator.ValidateAsync(templateType, data);
        if (!validationResult.IsValid)
        {
            return new DocumentGenerationResult
            {
                Success = false,
                ErrorMessage = $"数据验证失败: {validationResult.ErrorMessage}",
                ValidationErrors = validationResult.Errors
            };
        }
        
        // 2. 获取模板
        var template = await _templateRepository.GetTemplateAsync(templateType);
        if (template == null)
        {
            return new DocumentGenerationResult
            {
                Success = false,
                ErrorMessage = $"模板 '{templateType}' 不存在"
            };
        }
        
        // 3. 生成文档
        try
        {
            using var excelApp = ExcelFactory.CreateFrom(template.FilePath);
            var workbook = excelApp.ActiveWorkbook;
            var worksheet = workbook.ActiveSheetWrap;
            
            // 填充数据
            await FillTemplateDataAsync(worksheet, data, options);
            
            // 应用格式
            if (options.ApplyFormatting)
            {
                ApplyDocumentFormatting(worksheet, options.FormattingOptions);
            }
            
            // 生成输出文件名
            var outputFileName = GenerateOutputFileName(templateType, options);
            workbook.SaveAs(outputFileName);
            
            return new DocumentGenerationResult
            {
                Success = true,
                OutputFileName = outputFileName,
                GeneratedAt = DateTime.Now
            };
        }
        catch (Exception ex)
        {
            return new DocumentGenerationResult
            {
                Success = false,
                ErrorMessage = $"文档生成失败: {ex.Message}"
            };
        }
    }
    
    private async Task FillTemplateDataAsync(WorksheetWrapper worksheet, 
                                           Dictionary<string, object> data,
                                           DocumentGenerationOptions options)
    {
        foreach (var kvp in data)
        {
            var range = worksheet.Range(kvp.Key);
            
            // 支持异步数据转换
            var value = await ConvertDataAsync(kvp.Value, options);
            range.Value = value;
            
            // 记录修改历史
            if (options.TrackChanges)
            {
                worksheet.Cells[range.Row + 1, range.Column].Value = 
                    $"由系统生成于 {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
            }
        }
    }
    
    private void ApplyDocumentFormatting(WorksheetWrapper worksheet, FormattingOptions formatting)
    {
        // 应用字体格式
        if (formatting.FontSettings != null)
        {
            worksheet.Cells.Font.Name = formatting.FontSettings.FontName;
            worksheet.Cells.Font.Size = formatting.FontSettings.FontSize;
        }
        
        // 应用颜色主题
        if (formatting.ColorTheme != null)
        {
            // 实现颜色主题应用逻辑
        }
    }
    
    private string GenerateOutputFileName(string templateType, DocumentGenerationOptions options)
    {
        var timestamp = options.UseCustomTimestamp ? 
            options.CustomTimestamp : DateTime.Now;
        
        return $"{templateType}_{timestamp:yyyyMMddHHmmss}.xlsx";
    }
    
    private async Task<object> ConvertDataAsync(object value, DocumentGenerationOptions options)
    {
        // 实现数据转换逻辑
        await Task.CompletedTask;
        return value;
    }
}

// 支持接口和类
public interface ITemplateRepository
{
    Task<TemplateInfo> GetTemplateAsync(string templateType);
    Task SaveTemplateAsync(TemplateInfo template);
}

public interface IDataValidator
{
    Task<ValidationResult> ValidateAsync(string templateType, Dictionary<string, object> data);
}

public class DocumentGenerationResult
{
    public bool Success { get; set; }
    public string OutputFileName { get; set; }
    public DateTime GeneratedAt { get; set; }
    public string ErrorMessage { get; set; }
    public List<string> ValidationErrors { get; set; }
}

public class DocumentGenerationOptions
{
    public bool ApplyFormatting { get; set; } = true;
    public bool TrackChanges { get; set; } = false;
    public bool UseCustomTimestamp { get; set; } = false;
    public DateTime CustomTimestamp { get; set; }
    public FormattingOptions FormattingOptions { get; set; }
}

public class FormattingOptions
{
    public FontSettings FontSettings { get; set; }
    public ColorTheme ColorTheme { get; set; }
}

public class FontSettings
{
    public string FontName { get; set; } = "微软雅黑";
    public double FontSize { get; set; } = 11;
}

public class TemplateInfo
{
    public string TemplateType { get; set; }
    public string FilePath { get; set; }
    public DateTime CreatedAt { get; set; }
    public string Description { get; set; }
}

public class ValidationResult
{
    public bool IsValid { get; set; }
    public string ErrorMessage { get; set; }
    public List<string> Errors { get; set; }
}

// 使用示例
static async Task EnhancedTemplateSystemExample()
{
    // 创建依赖项(简化版)
    var templateRepository = new SimpleTemplateRepository();
    var dataValidator = new SimpleDataValidator();
    
    var system = new EnhancedTemplateApplicationSystem(templateRepository, dataValidator);
    
    // 生成文档
    var options = new DocumentGenerationOptions
    {
        ApplyFormatting = true,
        TrackChanges = true,
        FormattingOptions = new FormattingOptions
        {
            FontSettings = new FontSettings { FontName = "宋体", FontSize = 12 }
        }
    };
    
    var data = new Dictionary<string, object>
    {
        { "B2", "INV-2023-001" },
        { "B3", "张三" },
        { "B4", 1500.50m }
    };
    
    var result = await system.GenerateDocumentAsync("invoice", data, options);
    
    if (result.Success)
    {
        Console.WriteLine($"文档生成成功: {result.OutputFileName}");
    }
    else
    {
        Console.WriteLine($"文档生成失败: {result.ErrorMessage}");
    }
}

// 简化实现
public class SimpleTemplateRepository : ITemplateRepository
{
    public Task<TemplateInfo> GetTemplateAsync(string templateType)
    {
        var template = new TemplateInfo
        {
            TemplateType = templateType,
            FilePath = $"Templates/{templateType}_template.xlsx",
            CreatedAt = DateTime.Now,
            Description = $"{templateType} 模板"
        };
        
        return Task.FromResult(template);
    }
    
    public Task SaveTemplateAsync(TemplateInfo template)
    {
        return Task.CompletedTask;
    }
}

public class SimpleDataValidator : IDataValidator
{
    public Task<ValidationResult> ValidateAsync(string templateType, Dictionary<string, object> data)
    {
        var result = new ValidationResult { IsValid = true, Errors = new List<string>() };
        
        // 简单验证逻辑
        if (!data.ContainsKey("B2") || string.IsNullOrEmpty(data["B2"]?.ToString()))
        {
            result.IsValid = false;
            result.Errors.Add("B2字段不能为空");
        }
        
        return Task.FromResult(result);
    }
}

2.8 总结

本章详细介绍了Excel应用程序的创建与管理,涵盖了从基础创建方法到高级企业级应用的各个方面。通过本章的学习,您应该能够:

  1. 掌握核心创建方法:熟练使用BlankWorkbook、CreateInstance、CreateFrom、Open等方法
  2. 理解生命周期管理:正确管理应用程序实例的创建、使用和释放
  3. 应用性能优化:使用屏幕更新控制、批量操作等技术提升性能
  4. 实现健壮的错误处理:处理COM异常、实现重试机制
  5. 构建企业级应用:实现多实例管理、后台服务、模板系统等高级功能

关键要点回顾

  • 资源管理:始终使用using语句或正确的try-finally模式
  • 性能优化:禁用不需要的Excel功能,使用批量操作
  • 错误处理:针对COM异常的特殊处理,实现重试机制
  • 多实例策略:根据业务需求选择合适的实例管理方式

相关资源:

相关推荐
mudtools2 小时前
.NET驾驭Excel之力:自动化数据处理 - 开篇概述与环境准备
c#·自动化·.net·excel·wps
cx330上的猫2 小时前
【数据分析-Excel】常用函数汇总
数据分析·excel
唐青枫2 小时前
C#.NET WebAPI 返回类型深度解析:IActionResult 与 ActionResult<T> 的区别与应用
c#·.net
追逐时光者11 小时前
一个基于 .NET 8 + DDD 搭建的模块化微服务框架
后端·.net
William_cl11 小时前
C# ASP.NET MVC 数据验证实战:View 层双保险(Html.ValidationMessageFor + jQuery Validate)
后端·c#·asp.net·mvc
狮子不白12 小时前
C#WEB 防重复提交控制
开发语言·前端·程序人生·c#
Charles_go14 小时前
C#8、有哪些访问修饰符
java·前端·c#
yue00816 小时前
C# 求取整数的阶乘
java·开发语言·c#
企鹅侠客17 小时前
用AI写了一个Excel 批量插图工具
excel·excel批量插图