Dapper事务处理实战:如何避免ExecuteNonQuery中的Transaction属性未初始化错误

1. 从一次深夜报错说起:Transaction属性未初始化的“幽灵”

那天晚上十一点,我正赶着上线一个用户积分更新的功能。逻辑很简单:用户完成一个任务,系统要同时更新他的积分总额,并在积分明细表里插入一条记录。为了保证数据一致性,我自然想到了用事务。代码写得飞快,用的是咱们.NET圈里轻量又高效的Dapper。核心代码大概长这样:

using (var connection = new SqlConnection(connStr))
{
    connection.Open();
    var transaction = connection.BeginTransaction();

    try
    {
        // 更新用户总积分
        connection.Execute("UPDATE Users SET Points = Points + @AddedPoints WHERE UserId = @UserId",
                          new { UserId = userId, AddedPoints = points }, transaction);

        // 插入积分明细记录
        connection.Execute("INSERT INTO PointDetails (UserId, Points, Reason) VALUES (@UserId, @Points, @Reason)",
                          new { UserId = userId, Points = points, Reason = "完成任务奖励" });

        transaction.Commit();
        Console.WriteLine("操作成功!");
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        Console.WriteLine($"操作失败,已回滚: {ex.Message}");
    }
}

看起来没什么毛病对吧?我信心满满地跑起来,结果在第二句 Execute 那里,程序“啪”地一下抛出了一个异常,就是我标题里提到的那个:“如果分配给命令的连接位于本地挂起事务中,ExecuteNonQuery 要求命令拥有事务。命令的 Transaction 属性尚未初始化。”

当时我就懵了,明明开启了事务,也传给了第一个操作,怎么第二个就“属性未初始化”了?这错误信息对新手来说确实有点绕,它其实是说:你的数据库连接(Connection)已经处在一个本地挂起的事务(Local Pending Transaction)里了,但你现在要执行的这个命令(Command),它的 Transaction 属性是空的(null),系统不知道它该属于哪个事务,所以拒绝执行。

简单翻译成人话就是:你拉着连接进了一个事务的“房间”,然后让这个连接去干活(执行SQL),却没告诉它这次干活要遵守这个“房间”(事务)的规则。Dapper(或者说底层的ADO.NET)很严格,它不允许这种模糊状态,必须明确指定。这个错误在批量操作、循环插入或者像我这样连续执行多条不同SQL时特别容易踩坑。很多刚接触Dapper事务的朋友,包括当年的我,都在这儿栽过跟头。下面,我们就来把这个坑彻底填平。

2. 刨根问底:为什么Transaction会“丢失”?

要避免错误,首先得理解它为什么发生。我们得深入到Dapper和ADO.NET的协作层面去看。当你调用 connection.BeginTransaction() 时,本质上是在这个特定的数据库连接上启动了一个事务。这个事务对象(IDbTransaction)和这个连接是绑定的。

关键点在于 Dapper的 Execute 方法重载。Dapper提供了非常灵活的方法签名,其中与事务相关的典型重载是:Execute(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)

注意那个 IDbTransaction transaction = null 参数,它有一个默认值 null。这就是问题的根源!

场景还原:

  1. 你通过 BeginTransaction() 拿到了一个事务对象 trans
  2. 第一次调用 Execute(sql1, param, trans),你明确传入了 trans。Dapper在内部创建命令对象时,会把这个 trans 赋值给命令的 Transaction 属性。命令知道:“哦,我要在 trans 这个事务里执行。” 执行成功。
  3. 第二次调用 Execute(sql2, param),你省略</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值