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

如何解决ASP.NET Core Web API中Dapper工作单元(UnitOfWork)事务错误

解决Dapper工作单元模式下的Transaction未初始化异常

嘿,我一眼就看出问题所在了!你遇到的这个System.InvalidOperationException是因为你的数据库连接已经处于一个挂起的本地事务中,但执行查询命令时没有把事务关联到命令上。下面一步步给你讲清楚怎么修复:

问题根源

你的UnitOfWork已经在构造函数里开启了事务:

_transaction = _connection.BeginTransaction();

并且把这个事务传给了CityRepository,但在CityRepository.All()方法里调用QueryAsync时,只用到了Connection,没有把Transaction传递给Dapper的查询命令。当连接处于事务状态时,所有执行的命令必须关联到这个事务,否则就会抛出这个错误。

修复步骤

1. 给GenericRepository添加Transaction属性

首先确保你的基类GenericRepository<T>封装好事务引用,这样所有子类都能直接使用:

public abstract class GenericRepository<T>
{
    protected IDbConnection Connection { get; }
    protected IDbTransaction Transaction { get; }

    protected GenericRepository(IDbTransaction transaction)
    {
        Transaction = transaction ?? throw new ArgumentNullException(nameof(transaction));
        Connection = transaction.Connection ?? throw new InvalidOperationException("事务未关联有效连接");
    }
}

2. 修改CityRepository的All方法,传入Transaction

现在在CityRepository的查询里,把事务作为参数传给Dapper的QueryAsync:

internal class CityRepository : GenericRepository<City>, ICityRepository
{
    public CityRepository(IDbTransaction transaction) : base(transaction) { }

    public async Task<IEnumerable<City>> All()
    {
        // 关键:添加transaction参数关联当前事务
        var model = await Connection.QueryAsync<City>(
            "SELECT * FROM DT_Inspection.City",
            transaction: Transaction
        );
        return model.ToList();
    }
}

3. 完善UnitOfWork的IDisposable实现

你的UnitOfWork目前没有正确释放资源,这会导致连接和事务泄漏。实现IDisposable接口来确保资源被正确清理:

public class UnitOfWork : IUnitOfWork, IDisposable
{
    public IConfiguration configuration;
    private IDbTransaction _transaction;
    private IDbConnection _connection;
    ICityRepository _cityRepository;
    private bool _disposed;

    public UnitOfWork(IConfiguration _configuration)
    {
        configuration = _configuration;
        _connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection"));
        _connection.Open();
        _transaction = _connection.BeginTransaction();
    }

    public ICityRepository Cities
    {
        get { return _cityRepository ?? (_cityRepository = new CityRepository(_transaction)); }
    }

    public void Commit()
    {
        try
        {
            _transaction.Commit();
        }
        catch
        {
            _transaction.Rollback();
            throw;
        }
        finally
        {
            _transaction.Dispose();
            _transaction = _connection.BeginTransaction();
            resetRepositories();
        }
    }

    private void resetRepositories()
    {
        _cityRepository = null;
        // 如果有其他仓库,也要在这里重置为null
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // 先释放事务,再释放连接
            _transaction?.Dispose();
            _connection?.Dispose();
        }

        _disposed = true;
    }

    ~UnitOfWork()
    {
        Dispose(false);
    }
}

4. 注册正确的生命周期

在ASP.NET Core的依赖注入中,把IUnitOfWork注册为Scoped生命周期,这样每个HTTP请求对应一个UnitOfWork实例,请求结束后自动释放:

// 在Program.cs中
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();

为什么这样能解决问题?

通过把事务传递给Dapper的查询命令,相当于告诉数据库:“这个查询属于当前正在运行的事务”,这样连接就不会因为命令未关联事务而抛出异常了。同时完善的资源释放逻辑能避免连接泄漏,保证每个请求的资源都被正确清理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 04:12:52