事务(Transaction):一组SQL操作原子执行,要么全部成功提交(Commit),要么全部失败回滚(Rollback),满足ACID特性。
在.NET Framework/.NET中使用 SqlTransaction(SQL Server专属)实现。
流程要点:
- 打开数据库连接
conn.Open()- 通过连接开启事务
conn.BeginTransaction()- 将事务对象绑定到
SqlCommand.Transaction- 批量执行多条SQL
- 无异常:
tx.Commit();捕获异常:tx.Rollback()
例子
原图代码缺失catch回滚逻辑,完整可运行版本:
public static int ExecuteSqlTran(List<string> SQLStringList)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
SqlTransaction tx = conn.BeginTransaction();
cmd.Transaction = tx;
try
{
int count = 0;
for (int n = 0; n < SQLStringList.Count; n++)
{
string strsql = SQLStringList[n];
if (strsql.Trim().Length > 1)
{
cmd.CommandText = strsql;
count += cmd.ExecuteNonQuery();
}
}
tx.Commit(); // 全部执行成功,提交事务
return count;
}
catch (Exception ex)
{
tx.Rollback(); // 任意一条出错,整体回滚
throw ex; // 向外抛出异常,上层感知失败
}
}
}Code language: C# (cs)
这个版本直接拼接SQL字符串,存在SQL注入风险,适合理解事务原理;正式项目优先使用参数化查询
带参数化的事务示例
循环插入数据,使用SqlParameter防止注入,关键点:
- 循环末尾调用
sqlCom.Parameters.Clear()清空上一轮参数,避免参数堆积报错 - 同一个
SqlCommand对象复用,只修改CommandText与参数
SqlCommand sqlCom = new SqlCommand();
sqlCom.Connection = conn;
SqlTransaction st = conn.BeginTransaction();
sqlCom.Transaction = st;
int res = 0;
try
{
for (int i = 0; i < 10; i++)
{
sqlCom.CommandText = "INSERT INTO [dbo].[Article]([Title])VALUES(@Title)";
SqlParameter param = new SqlParameter("@Title", SqlDbType.NVarChar, 250);
param.Value = i + "这是新的标题" + DateTime.Now;
sqlCom.Parameters.Add(param);
res += sqlCom.ExecuteNonQuery();
sqlCom.Parameters.Clear(); // 清空参数,下一轮使用
}
st.Commit();
}
catch (Exception)
{
st.Rollback(); // 出现异常,全部撤销
}Code language: C# (cs)
1. 必须遵守的强制规则
BeginTransaction()必须在连接Open之后SqlCommand必须指定Transaction属性,否则会抛出异常Commit()/Rollback()之后事务自动结束,不能重复调用
其他
- .NET Core/.NET 5+ 推荐使用
using自动释放SqlTransaction - 超大量SQL批量场景,可以考虑
SqlBulkCopy,性能优于循环ExecuteNonQuery - 如果需要跨数据库事务,SqlTransaction无法支持,需要分布式事务(
TransactionScope)
Previous: ExecuteNonQuery
Next: SqlDataReader