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

来源:这里教程网 时间:2026-02-21 17:37:22 作者:

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<int>(sql, user, _uow.Transaction);
    }
}

这样测试时可 mock

IUnitOfWork
,业务逻辑不绑定具体数据库类型。

进阶提示:避免常见坑

别在仓储里调用
Open()
Close()
—— 连接生命周期由 UnitOfWork 管理
查询操作通常不需要事务,但若需跟写操作强一致(如读己写),才开启事务 异步支持要同步升级:添加
BeginTransactionAsync
CommitAsync
,并用
ExecuteAsync
替代同步方法
不建议自动提交:显式调用
Commit()
更清晰,防止遗漏或误提交

基本上就这些。Dapper 的简洁性正是优势——工作单元不用重造轮子,几行代码就能稳稳兜住事务边界。

相关推荐