You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在DDD模式与EF Core中实现悲观并发且分离业务与数据访问层?

业务层与数据访问层分离场景下悲观并发的实现方案

业务逻辑与数据访问层分离的架构下,完全可以正常使用悲观并发,核心实现思路是调整DbContext的生命周期管理逻辑,让DbContext的销毁时机和业务事务的结束时机绑定,而非在单个数据查询方法执行完成后就释放DbContext。


具体实现方案

1. 基于工作单元(Unit Of Work)模式实现(通用方案)

该方案适用于所有类型的项目,分层清晰,完全符合关注点分离原则:

  • 定义工作单元接口封装DbContext与事务控制,作为业务层和数据访问层的中间协调层
  • 整个业务操作流程共享同一个工作单元对应的DbContext实例,悲观锁的生命周期会和事务生命周期保持一致
  • 数据访问层(仓储)仅封装数据库操作逻辑,业务层仅处理业务校验和流程控制,互不感知对方的实现细节

示例代码如下:

工作单元接口定义

public interface IUnitOfWork : IDisposable
{
    IRepository<Order> OrderRepository { get; }
    IRepository<Item> ItemRepository { get; }
    int SaveChanges();
    void BeginTransaction();
    void CommitTransaction();
    void RollbackTransaction();
}

服务层业务逻辑实现

public Order AddItemToOrder(int orderId, Item paramItem)
{
    using var uow = _unitOfWorkFactory.Create();
    uow.BeginTransaction();

    // 调用仓储封装的带悲观锁的查询方法,锁会持续到事务结束
    var order = uow.OrderRepository.GetOrderWithUpdLock(orderId);

    // 业务校验完全放在业务层,不需要感知数据库锁逻辑
    if(order.Items.Count >= 20)
    {
        uow.RollbackTransaction();
        throw new InvalidOperationException("订单商品数量不能超过20");
    }

    order.Items.Add(paramItem);
    uow.SaveChanges();
    uow.CommitTransaction();

    return order;
}

仓储层(数据访问层)实现

public class OrderRepository : IRepository<Order>
{
    private readonly MyDbContext _dbContext;
    // 注入当前工作单元对应的同一个DbContext实例
    public OrderRepository(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 封装带悲观锁的查询逻辑,不同数据库的锁语法差异在这里处理
    public Order GetOrderWithUpdLock(int orderId)
    {
        var sql = "select * from [Order] where IDOrder = {0} with(UPDLOCK)";
        return _dbContext.Orders.FromRawSql(sql, orderId)
            .Include(x => x.Items)
            .FirstOrDefault();
    }
}

2. 基于IoC容器Scoped生命周期实现(Web项目适用)

如果是Web项目,可以直接利用IoC容器的生命周期管理简化实现:

  • 将DbContext注册为Scoped生命周期,同一个请求内共享同一个DbContext实例
  • 服务层的业务方法作为同一个请求内的逻辑,直接开启事务后调用仓储的加锁查询、执行业务逻辑、提交事务即可,悲观锁会在事务周期内持续生效

注意事项

  • 悲观锁的持有时间要尽可能短,不要在锁持有过程中执行耗时的非数据库操作,避免引发数据库死锁或性能问题
  • 不同数据库的悲观锁语法存在差异,比如MySQL用FOR UPDATE、SQL Server用WITH(UPDLOCK),这部分差异全部封装在数据访问层即可,业务层不需要感知

内容的提问来源于stack exchange,提问作者Álvaro García

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 17:09:00