SqlConnection在using块中是否关闭?单用户数据库报错疑问
问题描述
我编写了如下代码,遍历一系列SQL语句以更新SQL Server数据库中的多张表。第一条语句将数据库设置为单用户模式,确保不会有其他用户连接造成阻塞,最后一条语句将其恢复为多用户模式。
using (SqlConnection Connection = new SqlConnection(MyConn)) { try { Connection.Open(); // 打开连接 SqlCommand cmd; foreach (KeyValuePair<String, String> KVP in _SQL) { cmd = new SqlCommand(KVP.Key, Connection); cmd.CommandTimeout = 0; RowsAffected = cmd.ExecuteNonQuery(); } Connection.Close(); // 关闭连接 } catch (Exception e) { Log("Error : " + e.ToString()); } }
但近日我遇到了如下错误提示:
Database 'XXXXXXX' is already open and can only have one user at a time.
(翻译:数据库 'XXXXXXX' 已打开,同一时间只能有一个用户。)
疑问
SQL Server会在查询执行间隙断开连接吗?在此场景下,连接是否不会保持到调用Connection.Close()?
解答
关于连接是否会在间隙断开
不会。只要你没主动调用Connection.Close(),也没触发连接超时、服务器强制断开这类异常情况,SqlConnection会一直保持与SQL Server的连接,直到using块结束自动释放(或者你显式关闭)。
错误原因分析
你遇到的错误核心不是连接自动断开,而是以下两种常见情况:
- 连接池复用导致冲突:默认
SqlConnection启用连接池,当你打开连接时可能复用了之前未正确释放的旧连接。设置单用户模式后,连接池里的其他旧连接仍指向该数据库,后续执行语句时,这些旧连接试图访问单用户模式的数据库,就会触发错误。 - 单用户模式未强制断开其他连接:如果你的第一条
SET SINGLE_USER语句没加WITH ROLLBACK IMMEDIATE,SQL Server只会等待其他连接主动断开,不会强制终止。这时候如果有其他用户的连接还没断开,你的代码继续执行后续语句,就会因为数据库仍被其他用户占用而报错。
解决办法
- 修改单用户模式语句:加上
WITH ROLLBACK IMMEDIATE强制断开所有其他连接,确保你的连接成为唯一用户:ALTER DATABASE XXXXXXX SET SINGLE_USER WITH ROLLBACK IMMEDIATE; - 禁用连接池:在连接字符串中添加
Pooling=false,避免复用旧连接,确保全程只有当前连接访问数据库。 - 异常时恢复多用户模式:把恢复多用户的语句放到
finally块里,防止因为报错导致数据库一直卡在单用户状态:using (SqlConnection Connection = new SqlConnection(MyConn)) { try { Connection.Open(); SqlCommand cmd; foreach (KeyValuePair<String, String> KVP in _SQL) { cmd = new SqlCommand(KVP.Key, Connection); cmd.CommandTimeout = 0; RowsAffected = cmd.ExecuteNonQuery(); } } catch (Exception e) { Log("Error : " + e.ToString()); } finally { if (Connection.State == ConnectionState.Open) { using (var resetCmd = new SqlCommand("ALTER DATABASE XXXXXXX SET MULTI_USER;", Connection)) { resetCmd.ExecuteNonQuery(); } Connection.Close(); } } }
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

