必须显式调用connection.Open(),否则执行查询会抛InvalidOperationException;参数化查询是强制要求,禁用字符串拼接;SqlDataReader需及时读完或关闭;异步操作必须await,不可忽略返回的Task。

用 SqlConnection 和 SqlCommand 执行查询前必须开连接
不调用 connection.Open() 就直接执行 command.ExecuteReader() 或 command.ExecuteScalar(),会抛出 InvalidOperationException: "The connection is not open."。这不是异常处理不到位的问题,是逻辑前提没满足。
常见写法误区是把 Open() 放在 try 块里、或者和查询逻辑混在一起,导致连接状态不可控。正确做法是:显式打开、显式关闭,且优先用 using 确保释放。
using (var conn = new SqlConnection(connectionString)) { conn.Open(); /* 后续查询 */ }- 不要依赖
SqlCommand自动打开连接——它不会 - 如果连接字符串含
Connect Timeout=2,超时后抛SqlException,不是InvalidOperationException,注意区分错误类型
参数化查询必须用 @paramName,不能拼接字符串
写 "SELECT * FROM Users WHERE Name = '" + name + "'" 是高危操作,哪怕 name 看起来“只是个用户名”。SQL 注入可能通过 name = "admin'--" 绕过验证,或更隐蔽地触发隐式类型转换漏洞。
参数化不是“可选优化”,是强制要求。SQL Server 对 @ 开头的参数名做严格绑定,不参与语法解析,彻底隔离数据与指令。
- 添加参数用
command.Parameters.AddWithValue("@name", name),但注意AddWithValue可能推断错类型(比如把空字符串推成varchar(0)),生产环境建议用Parameters.Add("@name", SqlDbType.NVarChar).Value = name - 参数名必须和 SQL 中完全一致,大小写敏感(SQL Server 默认不区分,但作为习惯应保持一致)
- 存储过程调用也一样:
command.CommandType = CommandType.StoredProcedure后,仍需用@参数传值,不能靠顺序
ExecuteReader() 返回的 SqlDataReader 必须及时读完或关闭
没读完就跳出 while (reader.Read()) 循环,或忘了调用 reader.Close(),会导致连接被占用无法释放,后续请求堆积,最终抛 SqlException: "Timeout expired. The timeout period elapsed..." —— 实际是连接池耗尽,不是网络慢。
最稳妥的方式是把 SqlDataReader 也包进 using,但它本身不实现 IDisposable(.NET Framework)或只在 .NET Core+ 才实现,所以真正要 using 的是 SqlConnection,并在其作用域内完成所有读取。
- 用
reader.HasRows判断是否有结果前,必须先调用reader.Read()或reader.NextResult(),否则永远为false - 多结果集(如多个
SELECT)要用reader.NextResult()切换,不调就卡在第一个结果 - 字段访问推荐用索引(
reader[0])而非名称(reader["Id"]),后者有哈希查找开销,且名称错拼不报编译错
异步方法如 ExecuteReaderAsync() 必须 await,不能只调用
写 command.ExecuteReaderAsync(); 而不 await,等于扔了个 Task 不管,查询可能根本没执行,或在后台线程出错后静默失败。更糟的是,如果后续代码依赖查询结果,会拿到 null 或默认值,且堆栈里看不到 SQL 相关异常。
异步不是加个 Async 后缀就行,整个调用链得是 async/await 透传。ASP.NET Core 中尤其明显:Controller action 必须声明 async Task,不能返回 Task 而不 await。
-
await后得到的是SqlDataReader,它本身不是异步的,读取仍用同步的Read() - 若想边读边处理大结果集,可用
await using var reader = await command.ExecuteReaderAsync();(C# 8+) - 别在
async void方法里执行数据库操作——异常无法被捕获,会直接崩掉线程
连接生命周期、参数绑定、资源释放、异步等待,这四个点任意一个松动,都会在压测或上线后暴露。尤其是 SqlDataReader 的使用边界和 await 的完整性,最容易被忽略。











