在基于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