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 的简洁性正是优势——工作单元不用重造轮子,几行代码就能稳稳兜住事务边界。










