C# 操作SqlDataReader时出现调用Read报错、不关闭则ExecuteNonQuery失效的问题
错误原因与解决方案
你遇到的连环报错本质是ADO.NET使用逻辑冲突,核心问题出在连接资源被重复占用、对象生命周期管理混乱:
- 同一时间同一个
SqlConnection只能承载一个活动的数据库操作:要么开DataReader读数据,要么跑ExecuteNonQuery写数据,不能两个操作共用同一个打开的连接。 - 你代码里同时用
using块包裹连接、又在finally块里手动关闭连接,很容易出现连接提前被释放,导致还在运行的DataReader直接变为关闭状态,触发Invalid attempt to call Read when reader is closed的报错。 - 原本用
SqlDataAdapter.Fill()已经把查询的数据全部读到内存的DataTable里了,完全没必要再重复执行一次查询、开DataReader读一遍,重复操作不仅冗余,还直接造成了后续的连接占用冲突。
具体修正逻辑
- 彻底拆分读、写两个操作的连接:读操作把数据全部读到内存后自动释放连接,拿到用户的确认操作结果之后,再开新的连接跑存储过程的写入逻辑,完全规避单连接多操作的冲突。
- 所有实现了IDisposable的数据库对象(Connection、Command、DataReader)全部用
using块包裹,让框架自动管理资源释放,不要手动写finally块去关连接,避免和using的释放逻辑冲突。 - 把之前拼接SQL的写法全部换成参数化查询,彻底消除SQL注入风险。
- 日期类的参数直接传DateTime类型值,不要转成字符串再传,避免不同系统区域设置导致的格式解析错误。
修正后的参考代码
private readonly string connectionString = "你的数据库连接配置"; // 第一步:参数化查询匹配ISBN的图书信息 string isbnQuery = "SELECT book_id, title FROM book WHERE isbn = @Isbn"; DataTable dtbl = new DataTable(); using (SqlConnection queryConnection = new SqlConnection(connectionString)) { SqlCommand queryCommand = new SqlCommand(isbnQuery, queryConnection); queryCommand.Parameters.AddWithValue("@Isbn", txtbox_id.Text.Trim()); SqlDataAdapter sda = new SqlDataAdapter(queryCommand); sda.Fill(dtbl); } if (dtbl.Rows.Count != 1) { MessageBox.Show("未找到对应ISBN的图书"); return; } // 拿到查询结果后弹确认框,不需要占用数据库连接 DataRow matchedBook = dtbl.Rows[0]; DialogResult confirmResult = MessageBox.Show("Is this Correct?", matchedBook["title"].ToString(), MessageBoxButtons.YesNo); if (confirmResult == DialogResult.No) { return; } // 用户确认后,单独开连接跑写入逻辑 try { using (SqlConnection writeConnection = new SqlConnection(connectionString)) { SqlCommand logCommand = new SqlCommand("book_log", writeConnection); logCommand.CommandType = CommandType.StoredProcedure; char defaultStatus = 'N'; logCommand.Parameters.AddWithValue("@book_id", matchedBook["book_id"]); logCommand.Parameters.AddWithValue("@id", ID.getID); logCommand.Parameters.AddWithValue("@date_from", dtp_from.Value); logCommand.Parameters.AddWithValue("@date_to", dtp_hand.Value); logCommand.Parameters.AddWithValue("@fine", defaultStatus); logCommand.Parameters.AddWithValue("@handed", defaultStatus); writeConnection.Open(); logCommand.ExecuteNonQuery(); MessageBox.Show("Done!"); } } catch (SqlException ex) { MessageBox.Show("Contact a Staff Member " + ex.Message); }
额外注意事项
- 不要在
DataReader的遍历循环里用同一个连接执行其他写操作,哪怕开启了MultipleActiveResultSets的配置,也不如把数据读到内存后再做后续处理,逻辑更可控、更少出现状态异常。 - 不要在using块的外部重复编写手动Close连接的逻辑,using块本身会在代码块执行完成、或者抛出异常时自动调用Dispose方法把连接归还到连接池,手动重复关闭反而容易造成状态冲突。
内容的提问来源于stack exchange,提问作者Cameron Shearer
相关产品推荐
相关产品推荐

