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

SSIS脚本任务中异步上传文件如何追踪并记录完成任务

解决动态异步文件上传的日志记录问题

我明白你现在的困境——异步上传目录里的文件功能正常,但就是没法记录每个文件的上传完成日志,之前固定文件数的时候好好的,换成动态遍历就不行了。咱们一步步拆解问题,找到根源并解决:

核心问题分析

你用Task.WhenAny处理动态任务集合的逻辑,大概率踩了这几个常见坑:

  1. 非线程安全的任务集合:List<Task>不是线程安全的,当你在循环里调用tasks.Remove(completedTask)时,哪怕WhenAny每次只返回一个完成的任务,集合的修改操作也可能因为并发(比如多个任务几乎同时完成)导致意外行为,甚至跳过日志代码。
  2. 未处理任务的异常状态:如果某个UploadFiles任务抛出异常,Task.WhenAny会返回这个任务,但如果你直接去记录日志而不先等待/检查任务状态,任务的Faulted状态会中断后续代码执行,日志自然打不出来。
  3. SSIS上下文的线程切换问题:Dts.Events.FireInformation依赖SSIS的同步上下文,异步操作可能切换到其他线程,导致日志无法正确触发。

修正后的代码示例

下面是调整后的Main方法逻辑,解决了上述问题:

public async void Main()
{
    bool fireAgain = true;
    string bearerToken = GetBearerToken(); // 你的阻塞调用,没问题
    Dts.Events.FireInformation(0, "Initialization", "Bearer token obtained successfully", string.Empty, 0, ref fireAgain);

    string sourceDir = @"C:\Your\File\Directory";
    var tasks = new List<Task>();
    var lockObj = new object(); // 锁对象,保护任务集合的修改

    // 遍历目录,添加上传任务
    foreach (var filePath in Directory.GetFileSystemEntries(sourceDir))
    {
        var uploadTask = UploadFiles(filePath, bearerToken, Dts);
        // 把文件路径存在Task的AsyncState里,方便后续获取
        uploadTask.AsyncState = filePath;
        lock (lockObj)
        {
            tasks.Add(uploadTask);
        }
        Dts.Events.FireInformation(0, "Task Added", $"Queued upload for: {filePath}", string.Empty, 0, ref fireAgain);
    }

    // 循环处理完成的任务
    while (true)
    {
        Task completedTask;
        lock (lockObj)
        {
            if (tasks.Count == 0) break; // 没有任务了就退出循环
            completedTask = await Task.WhenAny(tasks);
            tasks.Remove(completedTask);
        }

        try
        {
            await completedTask; // 等待任务完成,捕获所有可能的异常
            string filePath = completedTask.AsyncState.ToString();
            Dts.Events.FireInformation(0, "Upload Complete", $"Successfully uploaded file: {filePath}", string.Empty, 0, ref fireAgain);
        }
        catch (Exception ex)
        {
            string filePath = completedTask.AsyncState.ToString();
            Dts.Events.FireError(0, "Upload Failed", $"Failed to upload file: {filePath}\nError Details: {ex.Message}", string.Empty, 0);
        }
    }

    Dts.Events.FireInformation(0, "Finalization", "All file upload tasks processed", string.Empty, 0, ref fireAgain);
    Dts.TaskResult = (int)ScriptResults.Success;
}

关键调整说明

  1. 线程安全的集合操作:用lock保护List<Task>的添加和移除操作,避免并发访问导致的集合损坏或逻辑跳过。
  2. 强制等待任务完成:通过await completedTask确保任务真正完成,同时捕获所有异常——哪怕任务失败,也能记录错误日志,不会跳过日志逻辑。
  3. 关联任务与文件路径:把文件路径存在Task.AsyncState里,这样在任务完成时能准确知道是哪个文件,避免日志里找不到对应文件的问题。

备选方案:如果日志还是不输出

如果调整后日志依然没反应,大概率是SSIS的同步上下文问题。可以在Main方法开头捕获同步上下文,然后在日志调用时切换回去:

// 在Main方法开头添加
var syncContext = SynchronizationContext.Current;

// 然后把日志调用改成:
syncContext.Post(_ => {
    bool fireAgain = true;
    Dts.Events.FireInformation(0, "Upload Complete", $"Successfully uploaded file: {filePath}", string.Empty, 0, ref fireAgain);
}, null);

这样能确保日志调用回到SSIS的原始线程上下文,解决跨线程调用的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:10:13