sqlconnection+sqlcommand+sqldatareader/sqldataadapter是最可控、最贴近sql本质的原生方式,适合调试、学习及高性能/事务场景,但需手动管理连接、参数化查询、资源释放与超时设置。

直接说结论:用 SqlConnection + SqlCommand + SqlDataReader 或 SqlDataAdapter 是最可控、最贴近 SQL 本质的原生方式,适合调试、学习和对性能/事务有明确要求的场景;但必须手动管理连接生命周期、参数防注入、资源释放,漏掉任何一环就容易出错。
SqlConnection 连接字符串写错或没开权限,连不上怎么办
常见错误现象是抛出 SqlException,消息里带 “A network-related or instance-specific error…” 或 “Login failed for user…”。
- 检查连接字符串中
Server(实例名)是否正确,本地默认实例可写.或(local),命名实例要带\实例名,比如DESKTOP-R6BQK8P\SQLEXPRESS -
Initial Catalog(数据库名)必须已存在,不能是空库名或拼错;SQL Server Express 默认不启用 TCP/IP,需在 SQL Server 配置管理器中打开 - Windows 身份验证用
Integrated Security=true;SQL 账户登录必须确保sa或指定用户已启用且密码正确,且服务器身份验证模式设为“SQL Server 和 Windows 身份验证模式” - 连接字符串不要硬编码在代码里,优先从
appsettings.json(.NET Core/5+)或App.config读取,避免提交敏感信息
执行 INSERT/UPDATE/DELETE 时返回值总是 0
这通常不是 SQL 写错了,而是 ExecuteNonQuery() 返回的是“受影响行数”,不是“成功与否”的布尔值。如果 SQL 语法合法但没匹配到数据(比如 WHERE 条件不成立),它就真返回 0。
- 确认 SQL 语句本身能独立在 SSMS 中跑通,尤其是 WHERE 子句是否真能命中记录
- 务必使用参数化查询,别拼字符串——
"UPDATE users SET name = '" + name + "' WHERE id = " + id是严重漏洞,也容易因单引号导致语法错误 - 正确写法是:
cmd.Parameters.AddWithValue("@name", name); cmd.Parameters.AddWithValue("@id", id);,然后 SQL 字符串里写UPDATE users SET name = @name WHERE id = @id - 如果需要获取刚插入的自增 ID,别用
SELECT @@IDENTITY,改用SELECT SCOPE_IDENTITY()并配合ExecuteScalar()
SqlDataReader 读完不 Close,程序卡死或内存暴涨
SqlDataReader 是连接态、只进、只读游标,它会一直占用底层连接,直到显式关闭或被 dispose。忘记关,后续操作就会阻塞在 Open() 上,或者引发“超时”“连接池已满”等错误。
- 永远用
using包裹:using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { ... } }—— 这能保证即使异常也会释放资源 - 不要在
while (reader.Read())循环里调reader.Close(),也不要在循环外手动Dispose(),using已涵盖 - 如果只是查单行,用
ExecuteScalar()更轻量;如果要加载整张表到内存做筛选/绑定,用SqlDataAdapter.Fill(DataTable)更合适 - 注意:DataReader 不支持多次遍历,读完就没了;想重复用,得重新执行命令或换用 DataTable
事务里多个 SqlCommand 共享同一个 SqlConnection 却报错
典型错误是 SqlConnection does not support parallel transactions 或 “There is already an open DataReader associated with this Connection”。根本原因是:一个 SqlConnection 同一时刻只能有一个活跃的命令(包括 Reader、ExecuteNonQuery 等)。
- 事务内所有命令必须复用同一个
SqlConnection实例,并显式传入SqlTransaction对象给每个SqlCommand的Transaction属性 - 不能在一个连接上一边
ExecuteReader(),一边又ExecuteNonQuery()—— 必须等 Reader 关闭后才能执行下一个命令 - 如果事务跨多个方法,把
SqlConnection和SqlTransaction当作参数传进去,而不是在每个方法里 new 新连接 - 别依赖连接池自动回收:事务未
Commit()或Rollback()就丢弃连接对象,连接池会把它标记为“不可重用”,造成连接泄漏
最常被忽略的一点:原生 ADO.NET 没有自动重试、没有连接健康检查、没有命令超时自动取消(除非显式设 CommandTimeout)。一旦网络抖动或 SQL 长时间卡住,整个线程就挂在那里。生产环境务必设 Connection Timeout=30 和 Command Timeout=60,并考虑封装一层带重试逻辑的执行器。










