Dapper不内置工作单元模式,但可自行封装UnitOfWork类统一管理连接与事务,通过IUnitOfWork接口解耦仓储,显式控制BeginTransaction/Commit/Rollback,确保操作原子性与可测试性。

Dapper 本身不内置工作单元(Unit of Work)模式,但你可以用轻量、可控的方式自己封装,实现事务一致性与仓储解耦。关键在于统一管理数据库连接、事务和命令执行时机——不是靠框架自动注入,而是靠设计把“一批操作当一个原子”来处理。
核心思路:用一个上下文协调连接、事务和仓储
不要让每个仓储自己开连接或事务。建一个 UnitOfWork 类,持有 IDbConnection 和可选的 IDbTransaction,所有仓储通过它获取连接(带事务),提交/回滚也只由它控制。
- 连接在 UnitOfWork 构造时打开,避免多次连接开销
- 事务在需要时显式开启(如调用
BeginTransaction()),不默认开启 - 仓储(如
UserRepository)接收IUnitOfWork而非IDbConnection,确保用的是同一连接和事务
简单实现示例(无依赖注入版)
以下是一个最小可行的 UnitOfWork 实现:
public interface IUnitOfWork : IDisposable { IDbConnection Connection { get; } IDbTransaction Transaction { get; } void BeginTransaction(); void Commit(); void Rollback(); } public class UnitOfWork : IUnitOfWork { private readonly IDbConnection _connection; public IDbConnection Connection => _connection; public IDbTransaction Transaction { get; private set; } public UnitOfWork(string connectionString) { _connection = new SqlConnection(connectionString); _connection.Open(); } public void BeginTransaction() => Transaction = _connection.BeginTransaction(); public void Commit() => Transaction?.Commit(); public void Rollback() => Transaction?.Rollback(); public void Dispose() { Transaction?.Dispose(); _connection?.Dispose(); } }
使用时:
using var uow = new UnitOfWork("conn-str"); uow.BeginTransaction(); var userRepo = new UserRepository(uow); var orderRepo = new OrderRepository(uow); userRepo.Insert(new User { Name = "Alice" }); orderRepo.Insert(new Order { UserId = 1, Amount = 100 }); uow.Commit(); // 一起提交,失败则全部回滚
配合仓储接口提升可测性与替换性
定义仓储接口,让具体实现只依赖 IUnitOfWork:
public interface IUserRepository { void Insert(User user); User GetById(int id); } public class UserRepository : IUserRepository { private readonly IUnitOfWork _uow; public UserRepository(IUnitOfWork uow) => _uow = uow; public void Insert(User user) { const string sql = "INSERT INTO Users (Name) VALUES (@Name); SELECT CAST(SCOPE_IDENTITY() as int)"; user.Id = _uow.Connection.QuerySingle(sql, user, _uow.Transaction); } }
这样测试时可 mock IUnitOfWork,业务逻辑不绑定具体数据库类型。
进阶提示:避免常见坑
- 别在仓储里调用
Open()或Close()—— 连接生命周期由 UnitOfWork 管理 - 查询操作通常不需要事务,但若需跟写操作强一致(如读己写),才开启事务
- 异步支持要同步升级:添加
BeginTransactionasync、CommitAsync,并用ExecuteAsync替代同步方法 - 不建议自动提交:显式调用
Commit()更清晰,防止遗漏或误提交
基本上就这些。Dapper 的简洁性正是优势——工作单元不用重造轮子,几行代码就能稳稳兜住事务边界。