EF Core如何实现工作单元模式 EF Core工作单元(Unit of Work)教程

26次阅读

DbContext 就是工作单元,因其内置变更追踪、原子提交、事务一致性等能力;仅当需解耦ORM、共享上下文、统一事务或便于Mock时,才需额外抽象IUnitOfWork接口

EF Core如何实现工作单元模式 EF Core工作单元(Unit of Work)教程

EF Core 本身已经内置了工作单元(Unit of Work)模式的核心行为——DbContext 就是一个天然的工作单元。它自动跟踪实体状态、批量提交变更、保证事务一致性,无需额外封装即可直接使用。所谓“实现工作单元模式”,更多是按需进行合理抽象和组织,而非从零造轮子。

为什么 DbContext 就是工作单元?

DbContext 在生命周期内:

  • 维护一份变更追踪(Change Tracker),记录所有 Add/Update/Remove 操作
  • 调用 SaveChanges() 或 SaveChangesAsync() 时,将所有待处理的变更一次性提交到数据库(原子性)
  • 默认在单个事务中执行(除非显式禁用或嵌套事务)
  • 支持手动开启事务(BeginTransaction)、回滚(Rollback)和提交(Commit)

何时需要进一步抽象 UoW?

当你有以下需求时,可考虑封装一层 UoW 接口:

  • 需要解耦业务逻辑与 EF Core 实现(例如未来可能切换 ORM)
  • 多个仓储(Repository)需共享同一上下文,确保跨仓储操作的一致性
  • 统一管理事务边界(尤其在 CQRS 或领域服务中)
  • 集成测试时方便 Mock 工作单元行为

注意:过度抽象可能增加复杂度,小项目通常直接用 DbContext 更清晰。

一个轻量实用的 UoW 实现

定义接口:

public interface IUnitOfWork : IDisposable {     DbContext Context { get; }     Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);     void BeginTransaction();     void Commit();     void Rollback(); }

实现类(复用现有 DbContext):

EF Core如何实现工作单元模式 EF Core工作单元(Unit of Work)教程

Canva

使用Canva可画,轻松创建专业设计

EF Core如何实现工作单元模式 EF Core工作单元(Unit of Work)教程 2603

查看详情 EF Core如何实现工作单元模式 EF Core工作单元(Unit of Work)教程

public class UnitOfWork : IUnitOfWork {     private readonly DbContext _context;     private IDbContextTransaction _transaction;      public UnitOfWork(DbContext context) => _context = context;      public DbContext Context => _context;      public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)         => await _context.SaveChangesAsync(cancellationToken);      public void BeginTransaction() =>          _transaction ??= _context.Database.BeginTransaction();      public void Commit()     {         _transaction?.Commit();         _transaction?.Dispose();         _transaction = null;     }      public void Rollback()     {         _transaction?.Rollback();         _transaction?.Dispose();         _transaction = null;     }      public void Dispose() => _transaction?.Dispose(); }

注册为 Scoped 服务(Startup.cs 或 Program.cs):

services.AddScoped<IUnitOfWork, UnitOfWork>(); services.AddDbContext<AppDbContext>(options =>      options.Usesqlserver(connectionString));

配合 Repository 使用的典型场景

确保多个 Repository 共享同一个 DbContext(即同一个 UoW):

public class OrderService {     private readonly IUnitOfWork _unitOfWork;     private readonly IOrderRepository _orderRepo;     private readonly IProductRepository _productRepo;      public OrderService(         IUnitOfWork unitOfWork,         IOrderRepository orderRepo,         IProductRepository productRepo)     {         _unitOfWork = unitOfWork;         _orderRepo = orderRepo;         _productRepo = productRepo;     }      public async Task PlaceOrderAsync(Order order)     {         _unitOfWork.BeginTransaction();         try         {             await _orderRepo.AddAsync(order);             var product = await _productRepo.GetByIdAsync(order.ProductId);             product.Stock -= order.Quantity;              await _unitOfWork.SaveChangesAsync();             _unitOfWork.Commit();         }         catch         {             _unitOfWork.Rollback();             throw;         }     } }

关键点:所有仓储构造函数应接收 DbContext 或 IUnitOfWork,而不是自行 new DbContext。

基本上就这些。EF Core 的工作单元不复杂但容易忽略其本质——它不是必须封装的“设计模式”,而是你每天都在用的 DbContext 的自然能力。

text=ZqhQzanResources