You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让WPF应用捕获CancellationToken抛出的OperationCanceledException?

问题分析
  1. Timer线程模型导致异常丢失:System.Timers.Timer的回调运行在线程池线程,抛出的OperationCanceledException无法传递到导出任务的上下文,且Timer会静默吞掉未处理异常,导致代码继续执行。
  2. Oracle中断异常覆盖取消异常:调用Cancel()后,Oracle客户端会先抛出自身的中断异常(如错误码1013),覆盖了CancellationToken抛出的OperationCanceledException。
解决方案

1. 移除Timer,在导出流程中直接检查取消信号

不要用Timer轮询,而是在导出的关键异步步骤(数据库连接、数据读取、Excel写入循环)中,主动调用token.ThrowIfCancellationRequested(),确保异常在导出任务线程中抛出。

类库导出方法示例:

public async Task ExportToExcelAsync(string oracleConnStr, CancellationToken token)
{
    using var conn = new OracleConnection(oracleConnStr);
    await conn.OpenAsync(token); // 带取消支持的异步连接

    using var cmd = new OracleCommand("SELECT * FROM TargetTable", conn);
    using var reader = await cmd.ExecuteReaderAsync(token); // 异步查询带取消

    // 初始化Excel工作簿/工作表逻辑
    while (await reader.ReadAsync(token))
    {
        token.ThrowIfCancellationRequested(); // 每读取一行检查取消
        // 将当前行数据写入Excel的逻辑
    }
}

2. WPF端正确捕获异常

在WPF中用异步任务执行导出操作,按先捕获取消异常、再处理Oracle特定异常、最后通用异常的顺序捕获,确保取消操作的提示优先展示。

WPF界面代码示例:

private CancellationTokenSource _exportCts;

private async void BtnExport_Click(object sender, RoutedEventArgs e)
{
    _exportCts = new CancellationTokenSource();
    BtnCancel.IsEnabled = true;

    try
    {
        await Task.Run(() => YourExportLibrary.ExportToExcelAsync("你的Oracle连接串", _exportCts.Token), _exportCts.Token);
        MessageBox.Show("导出完成");
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("导出已取消");
    }
    catch (OracleException ex)
    {
        // Oracle中断操作的错误码通常为1013,映射为取消提示
        if (ex.Number == 1013)
        {
            MessageBox.Show("导出已取消");
        }
        else
        {
            MessageBox.Show($"数据库错误:{ex.Message}");
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"导出失败:{ex.Message}");
    }
    finally
    {
        BtnCancel.IsEnabled = false;
        _exportCts.Dispose();
    }
}

private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
    _exportCts?.Cancel();
}

3. 关键注意事项

  • 必须使用Oracle客户端提供的异步方法+支持CancellationToken重载(如OpenAsync(token)、ExecuteReaderAsync(token)),确保取消信号能传递到Oracle操作层面。
  • 不要在UI线程执行导出操作,必须用Task.Run或IAsyncCommand将任务放到后台线程,避免阻塞UI。

内容的提问来源于stack exchange,提问作者Lucy82

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 22:03:29