在C#开发中,数据库连接和事务处理是数据访问层的核心。很多运行时故障,例如超时、死锁、数据不一致,都和这两块的实现方式有关。理解ADO.NET的连接池模型以及事务的生命周期,是避免常见问题的前提。

一、数据库连接常见的问题
最常见的问题是连接未正确释放。SqlConnection实现了IDisposable接口,如果不使用using语句,在异常发生时连接可能永远不会回到连接池。连接池默认最大连接数为100,高并发下很容易达到上限,后续请求会排队等待直到超时。
另一个容易被忽视的问题是连接字符串配置不当。例如没有设置Connection Timeout,或者使用了不合适的Pooling参数。下面的代码展示了错误写法:在循环里每次new连接却不关闭,会迅速耗尽资源。
// 错误示例:连接未释放
for (int i = 0; i < 1000; i++)
{
SqlConnection conn = new SqlConnection("Server=.;Database=test;Integrated Security=true");
conn.Open();
// 执行命令后未调用 conn.Close() 或 Dispose()
}
正确做法是用using确保释放,让连接自动归还池子。同时建议在连接字符串里明确最小和最大池大小,便于调优。
// 正确示例
string connStr = "Server=.;Database=test;Integrated Security=true;Max Pool Size=200;";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
// 执行操作
}
二、事务处理的典型错误
开发者常犯的一个错误是在事务中吞掉异常却不回滚。SqlTransaction不会自动回滚,如果代码捕获了异常但没调用Rollback,事务会一直持有锁,直到连接被回收才解除,这期间容易引发死锁。
还有人把事务范围开得太大,把不必要的查询也包进去,导致锁占用时间变长。下面代码演示了如何在C#里安全地使用事务:
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlTransaction tx = conn.BeginTransaction())
{
try
{
SqlCommand cmd1 = new SqlCommand("UPDATE Account SET Balance=Balance-100 WHERE Id=1", conn, tx);
cmd1.ExecuteNonQuery();
SqlCommand cmd2 = new SqlCommand("UPDATE Account SET Balance=Balance+100 WHERE Id=2", conn, tx);
cmd2.ExecuteNonQuery();
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
}
上面的结构保证了命令都关联到同一个事务对象,异常时必定回滚。注意SqlCommand的构造函数第三个参数就是事务,遗漏它会导致命令不在事务内执行。
三、异步与事务的配合要点
使用异步API能提升吞吐,但事务和连接不能跨异步上下文乱用。一个已开启的事务只在当前连接有效,如果await之后连接被别人用掉就会出错。推荐在同一个异步方法内完成开启、提交和回滚。
以下示例展示了异步事务写法,利用CancellationToken防止无限等待:
public async Task TransferAsync(CancellationToken ct)
{
using (SqlConnection conn = new SqlConnection(connStr))
{
await conn.OpenAsync(ct);
using (SqlTransaction tx = conn.BeginTransaction())
{
try
{
SqlCommand cmd = new SqlCommand("UPDATE Account SET Balance=Balance-50 WHERE Id=1", conn, tx);
await cmd.ExecuteNonQueryAsync(ct);
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
}
}
这种写法既利用了非阻塞IO,又通过using和try-catch保证了资源与数据一致性。在Web应用里,还可以把事务逻辑封装到工作单元模式中,进一步降低出错概率。
四、总结对照表
为了直观比较,下面列出常见问题与对应策略:
| 问题现象 | 根本原因 | 解决方式 |
|---|---|---|
| 连接超时 | 连接未释放导致池满 | 使用using释放,调大Max Pool Size |
| 数据不一致 | 异常未回滚事务 | try-catch中调用Rollback |
| 死锁频繁 | 事务粒度大、持有锁久 | 缩小事务范围,快速提交 |
把握这些要点后,C#中的数据访问代码会更加健壮,也能更轻松地应对生产环境的高并发与复杂业务场景。
C#数据库连接C#事务处理SqlTransaction修改时间:2026-08-07 15:24:29