SSIS中C#异步执行SQL查询(含存储过程)挂起问题求助
实现SSIS脚本任务中异步执行SQL存储过程并记录时间的方案
作为第一次接触C#异步编程的开发者,在SSIS脚本任务里搞异步确实容易踩坑,我来给你梳理一套可行的实现思路和代码示例,应该能帮到你:
核心思路拆解
- 用
SqlConnection和SqlCommand配合异步方法执行存储过程 - 为每个存储过程的执行创建独立异步任务,同时记录开始/结束时间
- 用
Task.WhenAll等待所有异步任务完成,避免阻塞主线程 - 将时间记录写入SQL日志表或SSIS内置日志(按需选择)
代码示例(SSIS脚本任务C#代码)
首先确保在脚本任务中引用System.Data.SqlClient(.NET Framework)或Microsoft.Data.SqlClient(.NET Core/5+),参考代码如下:
using System; using System.Data; using Microsoft.SqlServer.Dts.Runtime; using System.Data.SqlClient; using System.Threading.Tasks; namespace ST_XXXXXXXXX { [Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute] public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase { public void Main() { // 从SSIS变量获取数据库连接字符串(建议用配置管理) string connString = Dts.Variables["User::DBConnectionString"].Value.ToString(); // 待执行的存储过程列表 string[] spNames = new string[] { "usp_TestProc1", "usp_TestProc2", "usp_TestProc3" }; // 启动异步任务并等待完成(SSIS主线程为同步模型,需用Wait阻塞) ExecuteStoredProceduresAsync(connString, spNames).Wait(); Dts.TaskResult = (int)ScriptResults.Success; } private async Task ExecuteStoredProceduresAsync(string connectionString, string[] storedProcedures) { var tasks = new Task[storedProcedures.Length]; for (int i = 0; i < storedProcedures.Length; i++) { string spName = storedProcedures[i]; // 为每个存储过程创建独立异步任务 tasks[i] = ExecuteSingleSpWithLoggingAsync(connectionString, spName); } // 等待所有任务并行完成 await Task.WhenAll(tasks); } private async Task ExecuteSingleSpWithLoggingAsync(string connectionString, string spName) { DateTime startTime = DateTime.Now; string status = "Success"; string errorMsg = string.Empty; try { using (SqlConnection conn = new SqlConnection(connectionString)) { await conn.OpenAsync(); using (SqlCommand cmd = new SqlCommand(spName, conn)) { cmd.CommandType = CommandType.StoredProcedure; // 若存储过程需要参数,在此添加: // cmd.Parameters.AddWithValue("@ParamName", paramValue); await cmd.ExecuteNonQueryAsync(); } } } catch (Exception ex) { status = "Failed"; errorMsg = ex.Message; // 可在此添加额外错误处理逻辑 } finally { DateTime endTime = DateTime.Now; TimeSpan duration = endTime - startTime; // 调用日志记录方法,将执行信息写入数据库 await LogExecutionResultAsync(connectionString, spName, startTime, endTime, duration.TotalSeconds, status, errorMsg); } } private async Task LogExecutionResultAsync(string connectionString, string spName, DateTime startTime, DateTime endTime, double durationSeconds, string status, string errorMsg) { using (SqlConnection conn = new SqlConnection(connectionString)) { await conn.OpenAsync(); using (SqlCommand cmd = new SqlCommand("usp_LogSpExecution", conn)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@StoredProcedureName", spName); cmd.Parameters.AddWithValue("@StartTime", startTime); cmd.Parameters.AddWithValue("@EndTime", endTime); cmd.Parameters.AddWithValue("@DurationSeconds", durationSeconds); cmd.Parameters.AddWithValue("@Status", status); cmd.Parameters.AddWithValue("@ErrorMessage", errorMsg); await cmd.ExecuteNonQueryAsync(); } } } enum ScriptResults { Success = 0, Failure = 1 } } }
关键注意事项
- SSIS异步限制:SSIS脚本任务主线程为同步模型,需用
.Wait()等待异步任务完成,避免脚本提前终止 - 连接池优化:用
using语句包裹SqlConnection和SqlCommand,自动释放连接以利用SQL Server连接池 - 日志表准备:需提前创建日志表或对应的存储过程
usp_LogSpExecution,示例表结构:CREATE TABLE SpExecutionLog ( LogId INT IDENTITY(1,1) PRIMARY KEY, StoredProcedureName NVARCHAR(128) NOT NULL, StartTime DATETIME NOT NULL, EndTime DATETIME NOT NULL, DurationSeconds FLOAT NOT NULL, Status NVARCHAR(10) NOT NULL, ErrorMessage NVARCHAR(MAX) NULL, CreatedAt DATETIME DEFAULT GETDATE() ) - 异常隔离:每个异步任务单独捕获异常,避免单个存储过程失败导致所有任务中断
- 参数适配:若存储过程需传参,在
ExecuteSingleSpWithLoggingAsync中添加对应SqlParameter即可
模拟测试验证
你已经用模拟存储过程测试过,可以在模拟过程中添加WAITFOR DELAY '00:00:05'这样的延迟,验证异步执行是否真的并行运行,同时查看日志中的起止时间是否符合预期。
内容的提问来源于stack exchange,提问作者Garry Cotton




