You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中多方法间使用Dapper事务,如何避免传递连接与事务参数

无需传递SqlConnection/SqlTransaction的Dapper事务实现方案

方案1:类级字段存储连接与事务

在业务类中添加私有字段临时存储当前的连接和事务,开启事务时赋值,操作方法优先使用这些字段,事务结束后重置状态。

public class OrderItemService
{
    private SqlConnection _currentConnection;
    private SqlTransaction _currentTransaction;

    public async Task<bool> OrderItemChange(int orderItemId)
    {
        _currentConnection = await Connection.GetOpenConnection();
        _currentTransaction = _currentConnection.BeginTransaction();
        try
        {
            await Find(orderItemId);
            await Remove(orderItemId);
            await Save(orderItemId);

            _currentTransaction.Commit();
            return true;
        }
        catch (Exception ex)
        {
            _currentTransaction.Rollback();
            return false;
        }
        finally
        {
            _currentTransaction?.Dispose();
            _currentConnection?.Dispose();
            // 重置字段避免后续方法误用
            _currentConnection = null;
            _currentTransaction = null;
        }
    }

    private async Task Find(int orderItemId)
    {
        // 优先使用类内的连接和事务,无则创建新连接
        var conn = _currentConnection ?? await Connection.GetOpenConnection();
        var trans = _currentTransaction;

        var item = await conn.QueryFirstOrDefaultAsync<OrderItem>(
            "SELECT * FROM OrderItems WHERE Id = @Id", 
            new { Id = orderItemId }, 
            transaction: trans);
        // ...业务逻辑
    }

    // Remove和Save方法同理,复用conn和trans字段
}

优缺点:实现简单,适配单一类内的事务场景;但类的状态会被事务方法临时污染,必须在finally中重置,多线程/异步并发场景需谨慎。

方案2:AsyncLocal实现隐式事务上下文

用AsyncLocal<T>存储当前异步上下文的连接和事务,同一调用链上的所有方法可自动获取,无需手动传参。

public static class TransactionContext
{
    private static readonly AsyncLocal<(SqlConnection Conn, SqlTransaction Trans)> _context = new();

    public static void Set(SqlConnection conn, SqlTransaction trans)
    {
        _context.Value = (conn, trans);
    }

    public static (SqlConnection Conn, SqlTransaction Trans) Get()
    {
        return _context.Value;
    }

    public static void Clear()
    {
        _context.Value = default;
    }
}

public class OrderItemService
{
    public async Task<bool> OrderItemChange(int orderItemId)
    {
        using var conn = await Connection.GetOpenConnection();
        using var trans = conn.BeginTransaction();
        TransactionContext.Set(conn, trans);
        try
        {
            await Find(orderItemId);
            await Remove(orderItemId);
            await Save(orderItemId);

            trans.Commit();
            return true;
        }
        catch (Exception ex)
        {
            trans.Rollback();
            return false;
        }
        finally
        {
            TransactionContext.Clear();
        }
    }

    private async Task Find(int orderItemId)
    {
        var (conn, trans) = TransactionContext.Get();
        // 上下文无连接时,说明不在事务中,创建新连接
        if (conn == null)
        {
            conn = await Connection.GetOpenConnection();
            trans = null;
        }

        var item = await conn.QueryFirstOrDefaultAsync<OrderItem>(
            "SELECT * FROM OrderItems WHERE Id = @Id", 
            new { Id = orderItemId }, 
            transaction: trans);
        // ...业务逻辑
    }
}

优缺点:支持跨类的事务传递,异步上下文安全;需要额外的上下文类,复杂度稍高,但复用性强。

方案3:使用TransactionScope(.NET Core 2.1+)

TransactionScope可自动管理事务,Dapper会自动识别当前事务范围,无需手动传递事务参数。异步场景下需指定TransactionScopeAsyncFlowOption.Enabled。

public async Task<bool> OrderItemChange(int orderItemId)
{
    // 开启异步流支持,避免事务上下文丢失
    using var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    try
    {
        await Find(orderItemId);
        await Remove(orderItemId);
        await Save(orderItemId);

        scope.Complete();
        return true;
    }
    catch (Exception ex)
    {
        // 不调用Complete则自动回滚
        return false;
    }
}

private async Task Find(int orderItemId)
{
    using var conn = await Connection.GetOpenConnection();
    // Dapper自动关联当前TransactionScope
    var item = await conn.QueryFirstOrDefaultAsync<OrderItem>(
        "SELECT * FROM OrderItems WHERE Id = @Id", 
        new { Id = orderItemId });
    // ...业务逻辑
}

优缺点:代码最简洁,无需修改原有方法;但如果操作涉及多个数据库会触发分布式事务(MSDTC),可能带来性能开销,需根据场景评估。

内容的提问来源于stack exchange,提问作者Farzad Karimi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:47:42