调用ExecuteAsync抛出Connection is closed异常的原因及优化咨询
我仓储类中有如下方法:
public async void UpdateName(int id, string name) { var connection = dataContext.GetDatabase().Connection; var initialState = connection.State; try { if (initialState == ConnectionState.Closed) await connection.OpenAsync(); await connection.ExecuteAsync("myproc", new { id, name}, commandType: CommandType.StoredProcedure); } catch { throw; } finally { if (initialState == ConnectionState.Closed) await connection.CloseAsync(); } }
调用ExecuteAsync时会抛出Connection is closed异常,但调试时发现调用前连接状态为Open。另外我发现ExecuteAsync的实现本身也会尝试打开连接。现咨询两个问题:
- 该异常产生的原因是什么?
- 是否可以移除方法中自定义的连接打开/关闭逻辑?
更新:堆栈跟踪信息如下:
at System.Data.SqlClient.SqlCommand.EndExecuteNonQuery(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location ---
at Dapper.SqlMapper.d__39.MoveNext()
at MyApp.MyRepository.d__5.MoveNext() in C:\myapp\myrepository.cs:line 91
at System.Threading.Tasks.Task.<>c.b__128_1(Object state)
at System.Threading.QueueUserWorkItemCallback.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
1. 异常产生的原因
核心问题出在**async void的使用**和连接状态的竞态条件:
async void属于“火即忘”的异步方法,调用方无法跟踪其执行生命周期,线程池可能在方法执行完成前就回收相关资源,导致连接在ExecuteAsync执行过程中被意外关闭。- 你通过
initialState记录了连接初始状态,但异步执行过程中,其他共享dataContext的线程可能修改了连接状态——比如在OpenAsync之后、ExecuteAsync之前,连接被其他逻辑关闭。 - 虽然Dapper的
ExecuteAsync会自动处理连接打开,但它依赖的是调用瞬间的连接状态判断。如果你的手动打开操作后,连接被外部逻辑关闭,Dapper执行时就会遇到已关闭的连接,抛出异常。
2. 是否可以移除自定义的连接打开/关闭逻辑?
完全可以,甚至强烈建议移除,理由如下:
- Dapper的所有异步执行方法(包括
ExecuteAsync)内部都会自动处理连接生命周期:如果连接处于关闭状态,它会打开连接,执行完操作后再恢复到原来的状态。 - 当前的手动逻辑不仅冗余,还会引入竞态风险——比如你手动打开的连接,可能在Dapper执行期间被其他代码或
async void的线程问题意外关闭。 - 移除后,代码可简化为(同时必须修正
async void为async Task,这是异步方法的标准写法,避免线程资源异常):
public async Task UpdateName(int id, string name) { var connection = dataContext.GetDatabase().Connection; await connection.ExecuteAsync("myproc", new { id, name}, commandType: CommandType.StoredProcedure); }
内容的提问来源于stack exchange,提问作者Ivan Debono

