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

18次阅读

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

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

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 管理
  • 查询操作通常不需要事务,但若需跟写操作强一致(如读己写),才开启事务
  • 异步支持要同步升级:添加 BeginTransactionasyncCommitAsync,并用 ExecuteAsync 替代同步方法
  • 不建议自动提交:显式调用 Commit() 更清晰,防止遗漏或误提交

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

text=ZqhQzanResources