Dapper如何实现工作单元模式 Unit of Work with Dapper教程

来源:AI技术网作者:松本一香头衔:网络博主
导读:本期聚焦于小伙伴创作的《Dapper如何实现工作单元模式 Unit of Work with Dapper教程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Dapper如何实现工作单元模式 Unit of Work with Dapper教程》有用,将其分享出去将是对创作者最好的鼓励。

在基于Dapper的数据库操作开发中,当我们需要执行多个关联的增删改操作时,比如创建订单同时扣减库存,就需要保证这些操作要么全部成功,要么全部失败,工作单元模式就是用来管理这类场景的设计模式,它可以将多个数据库操作纳入同一个事务上下文,统一控制提交和回滚。

Dapper如何实现工作单元模式 Unit of Work with Dapper教程

工作单元模式核心概念

工作单元模式的核心作用是维护一个业务用例中涉及的所有数据库操作,跟踪这些操作的变更状态,最后统一提交所有变更。在Dapper场景下,它的核心能力包括:

  • 管理数据库连接和事务的创建与释放
  • 提供统一的数据库操作入口,封装Dapper的CRUD方法
  • 控制事务的提交和回滚,保证操作的原子性
  • 避免重复创建连接和事务,提升资源利用率

基础接口定义

首先我们定义工作单元的核心接口,包含事务控制方法和数据库操作入口:

public interface IUnitOfWork : IDisposable
{
    // 获取当前工作单元对应的数据库连接
    IDbConnection Connection { get; }
    
    // 获取当前事务
    IDbTransaction Transaction { get; }
    
    // 开始事务
    void BeginTransaction();
    
    // 提交事务
    void Commit();
    
    // 回滚事务
    void Rollback();
    
    // 通用的查询方法,封装Dapper的Query方法
    Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null);
    
    // 通用的执行方法,封装Dapper的Execute方法
    Task<int> ExecuteAsync(string sql, object param = null);
    
    // 获取指定实体的仓储(可选设计,可根据需求调整)
    IRepository<T> GetRepository<T>() where T : class;
}

工作单元具体实现

接下来实现上述接口,核心逻辑是管理连接和事务的生命周期,所有数据库操作都基于同一个连接和事务:

public class DapperUnitOfWork : IUnitOfWork
{
    private readonly IDbConnection _connection;
    private IDbTransaction _transaction;
    private bool _disposed = false;
    private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();
    
    public DapperUnitOfWork(string connectionString)
    {
        // 创建SQL Server连接,可根据实际数据库类型调整
        _connection = new SqlConnection(connectionString);
        _connection.Open();
    }
    
    public IDbConnection Connection => _connection;
    
    public IDbTransaction Transaction => _transaction;
    
    public void BeginTransaction()
    {
        if (_transaction != null)
        {
            throw new InvalidOperationException("事务已经开启,无法重复开启");
        }
        _transaction = _connection.BeginTransaction();
    }
    
    public void Commit()
    {
        try
        {
            _transaction?.Commit();
        }
        catch
        {
            Rollback();
            throw;
        }
        finally
        {
            _transaction?.Dispose();
            _transaction = null;
        }
    }
    
    public void Rollback()
    {
        try
        {
            _transaction?.Rollback();
        }
        finally
        {
            _transaction?.Dispose();
            _transaction = null;
        }
    }
    
    public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null)
    {
        return await _connection.QueryAsync<T>(sql, param, _transaction);
    }
    
    public async Task<int> ExecuteAsync(string sql, object param = null)
    {
        return await _connection.ExecuteAsync(sql, param, _transaction);
    }
    
    public IRepository<T> GetRepository<T>() where T : class
    {
        var type = typeof(T);
        if (!_repositories.ContainsKey(type))
        {
            // 这里可以注入具体的仓储实现,此处为简化示例直接创建
            _repositories[type] = new DapperRepository<T>(this);
        }
        return (IRepository<T>)_repositories[type];
    }
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _transaction?.Dispose();
                _connection?.Dispose();
            }
            _disposed = true;
        }
    }
}

配套仓储接口与实现

为了更规范地管理实体操作,我们可以补充简单的仓储层,让工作单元和实体操作解耦:

// 仓储通用接口
public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(int id);
    Task<int> InsertAsync(T entity);
    Task<int> UpdateAsync(T entity);
    Task<int> DeleteAsync(int id);
}

// 仓储实现
public class DapperRepository<T> : IRepository<T> where T : class
{
    private readonly IUnitOfWork _unitOfWork;
    
    public DapperRepository(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
    
    public async Task<T> GetByIdAsync(int id)
    {
        var tableName = typeof(T).Name;
        var sql = $"SELECT * FROM {tableName} WHERE Id = @Id";
        return await _unitOfWork.Connection.QueryFirstOrDefaultAsync<T>(sql, new { Id = id }, _unitOfWork.Transaction);
    }
    
    public async Task<int> InsertAsync(T entity)
    {
        var tableName = typeof(T).Name;
        // 简化示例,实际场景需要根据实体属性生成插入语句
        var properties = typeof(T).GetProperties().Where(p => p.Name != "Id").Select(p => p.Name);
        var columnNames = string.Join(",", properties);
        var paramNames = string.Join(",", properties.Select(p => "@" + p));
        var sql = $"INSERT INTO {tableName} ({columnNames}) VALUES ({paramNames})";
        return await _unitOfWork.ExecuteAsync(sql, entity);
    }
    
    public async Task<int> UpdateAsync(T entity)
    {
        var tableName = typeof(T).Name;
        var properties = typeof(T).GetProperties().Where(p => p.Name != "Id").Select(p => $"{p.Name} = @{p.Name}");
        var setClause = string.Join(",", properties);
        var sql = $"UPDATE {tableName} SET {setClause} WHERE Id = @Id";
        return await _unitOfWork.ExecuteAsync(sql, entity);
    }
    
    public async Task<int> DeleteAsync(int id)
    {
        var tableName = typeof(T).Name;
        var sql = $"DELETE FROM {tableName} WHERE Id = @Id";
        return await _unitOfWork.ExecuteAsync(sql, new { Id = id });
    }
}

实际使用示例

下面展示如何在业务代码中使用工作单元保证多个操作的原子性,比如创建订单同时扣减库存的场景:

public class OrderService
{
    private readonly string _connectionString = "Server=localhost;Database=TestDb;Trusted_Connection=True;";
    
    public async Task CreateOrderWithDeductStock(int productId, int quantity, int orderUserId)
    {
        using (var unitOfWork = new DapperUnitOfWork(_connectionString))
        {
            try
            {
                // 开启事务
                unitOfWork.BeginTransaction();
                
                // 获取商品仓储和订单仓储
                var productRepo = unitOfWork.GetRepository<Product>();
                var orderRepo = unitOfWork.GetRepository<Order>();
                
                // 1. 查询商品库存
                var product = await productRepo.GetByIdAsync(productId);
                if (product.Stock < quantity)
                {
                    throw new Exception("库存不足");
                }
                
                // 2. 扣减库存
                product.Stock -= quantity;
                await productRepo.UpdateAsync(product);
                
                // 3. 创建订单
                var order = new Order
                {
                    UserId = orderUserId,
                    ProductId = productId,
                    Quantity = quantity,
                    CreateTime = DateTime.Now
                };
                await orderRepo.InsertAsync(order);
                
                // 所有操作成功,提交事务
                unitOfWork.Commit();
                Console.WriteLine("订单创建成功,库存扣减完成");
            }
            catch (Exception ex)
            {
                // 操作失败,回滚事务
                unitOfWork.Rollback();
                Console.WriteLine($"操作失败,已回滚:{ex.Message}");
            }
        }
    }
}

// 示例实体类
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Stock { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public int ProductId { get; set; }
    public int Quantity { get; set; }
    public DateTime CreateTime { get; set; }
}

注意事项

在实际使用Dapper工作单元模式时,需要注意以下几点:

  • 连接字符串需要根据实际使用的数据库调整,上述示例使用的是SQL Server,如果是MySQL可以替换为MySqlConnection
  • 仓储的SQL生成逻辑仅为示例,实际项目中可以使用Dapper的扩展库或者自定义特性来生成更通用的SQL
  • 工作单元的生命周期建议和业务用例保持一致,使用using包裹确保资源释放
  • 如果项目使用依赖注入框架,可以将工作单元注册为作用域生命周期,避免手动创建和释放

DapperUnit_of_Work工作单元模式ADO.NET事务管理修改时间:2026-06-14 11:48:23

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。