如何解决Dapper调用长时间运行存储过程的超时错误
解决ASP.NET Core 6 + Dapper调用长耗时存储过程的超时问题
核心问题分析
- 方法命名为
GetRiskByYearAsync却使用同步Query方法,违背异步编程规范,导致ASP.NET Core请求上下文阻塞,触发超时。 - 存储过程在SSMS中耗时11分钟(660秒),但之前要么用默认30秒超时(
commandTimeout: null),要么设置900秒时因同步/异步混用导致连接提前释放,无法获取结果。 - 即使命令超时设置足够,ASP.NET Core或IIS的全局请求超时限制也可能截断请求。
分步解决方案
1. 修正异步方法实现
替换同步Query为异步QueryAsync,正确使用await,方法返回Task<IEnumerable<ProjectDetailsByYear>>:
public async Task<IEnumerable<ProjectDetailsByYear>> GetRiskByYearAsync(ReportsConfigurationVM reportsConfigurationVM) { var parameters = new DynamicParameters(); parameters.Add("@ProjectDetailsID", reportsConfigurationVM.ProjectDetailsID, DbType.String); parameters.Add("@ProjectNumber", reportsConfigurationVM.ProjectNumber, DbType.String); parameters.Add("@ProjectName", reportsConfigurationVM.ProjectName, DbType.String); using (var connection = new SqlConnection(_connectionString)) { await connection.OpenAsync(); var commandDef = new CommandDefinition( sql: "proc_GetProjectDetailsByYear", parameters: parameters, commandType: CommandType.StoredProcedure, commandTimeout: 720 // 设为12分钟,比存储过程耗时多留缓冲 ); var projectDetails = await connection.QueryAsync<ProjectDetailsByYear>(commandDef); return projectDetails; } }
2. 调整ASP.NET Core全局超时设置
默认Kestrel和IIS的请求超时远短于11分钟,需手动修改:
Kestrel配置(Program.cs)
builder.WebHost.ConfigureKestrel(serverOptions => { serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(15); serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(15); });
IIS配置(web.config)
部署到IIS时添加请求超时设置:
<system.webServer> <aspNetCore requestTimeout="00:15:00" processPath="dotnet" arguments=".\YourProject.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" /> </system.webServer>
3. 检查SQL Server远程查询超时
SQL Server默认远程查询超时为600秒(10分钟),小于存储过程耗时,需调整:
在SSMS中执行(需管理员权限):
sp_configure 'remote query timeout', 720; -- 设置为12分钟 GO RECONFIGURE; GO
若需关闭超时限制,可设为0。
4. 优化存储过程(关键建议)
11分钟的执行时间过长,建议:
- 检查执行计划,添加缺失索引,避免全表扫描。
- 优化查询逻辑,若结果集过大,考虑分页返回或开启Dapper流式处理(设置
buffered: false)。 - 排查参数嗅探问题,在存储过程末尾添加
OPTION (RECOMPILE)强制重新生成执行计划。
为什么之前设置commandTimeout:900无结果?
异步方法中使用同步Query会阻塞ASP.NET Core请求上下文,耗尽线程池资源,导致请求被提前终止、连接释放,无法获取存储过程的结果集。改用异步QueryAsync并await后,连接会保持到查询完成。
内容的提问来源于stack exchange,提问作者Massey
相关产品推荐
相关产品推荐

