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

ASP.NET Core中从控制器传递Unit of Work仓储到应用层是否合规?

关于Clean Architecture中从控制器传递Unit of Work到应用层的规范问题

你的实现虽然能正常运行,但不符合Clean Architecture的核心依赖规则,主要问题在于破坏了层间依赖方向和职责边界,下面具体分析并给出正确的实现方式:

核心问题:违反依赖规则

Clean Architecture的核心原则是所有依赖必须指向内部层:领域层(最内层)→ 应用层 → 接口适配器层(控制器属于这一层)→ 最外层(基础设施、UI等)。

你从控制器(外层)传递Unit of Work到应用层(内层),相当于让应用层依赖于外层的具体实现,这完全颠倒了依赖方向——应用层应该只依赖抽象(比如IUnitOfWork),而不是由外层传递具体实例。

这种做法的额外问题

  • 测试复杂度提升:测试应用服务时,需要手动构造并传递Unit of Work实例,无法通过DI轻松注入模拟实现,增加了测试成本。
  • 职责混乱:控制器的职责是处理HTTP请求、参数校验和返回响应,不应该负责管理Unit of Work的传递逻辑。
  • 耦合度增加:应用层和控制器强耦合,后续修改控制器或Unit of Work的实现时,可能需要同步修改应用层代码。

正确的实现方式

1. 定义Unit of Work抽象

将IUnitOfWork抽象放在领域层或应用层(更推荐领域层,因为Unit of Work是领域概念的一部分):

public interface IUnitOfWork
{
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    // 暴露领域Repository的抽象,而非具体实现
    IUserRepository Users { get; }
    IOrderRepository Orders { get; }
}

2. 在基础设施层实现抽象

基于EF Core(或其他ORM)实现IUnitOfWork,放在基础设施层:

public class EfCoreUnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _dbContext;
    private IUserRepository _userRepository;
    private IOrderRepository _orderRepository;

    public EfCoreUnitOfWork(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        return await _dbContext.SaveChangesAsync(cancellationToken);
    }

    public IUserRepository Users
    {
        get => _userRepository ??= new EfCoreUserRepository(_dbContext);
    }

    public IOrderRepository Orders
    {
        get => _orderRepository ??= new EfCoreOrderRepository(_dbContext);
    }
}

3. 应用层依赖抽象并通过DI注入

应用服务直接依赖IUnitOfWork抽象,由DI容器注入实现:

public class UserAppService
{
    private readonly IUnitOfWork _unitOfWork;

    public UserAppService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task CreateUserAsync(UserCreateDto dto)
    {
        // 领域逻辑转换
        var user = new User(dto.FullName, dto.Email);
        await _unitOfWork.Users.AddAsync(user);
        // 可在此处添加更多领域操作,最后统一提交
        await _unitOfWork.SaveChangesAsync();
    }
}

4. 控制器仅调用应用服务

控制器只依赖应用服务,无需关心Unit of Work的细节:

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
    private readonly UserAppService _userAppService;

    public UsersController(UserAppService userAppService)
    {
        _userAppService = userAppService;
    }

    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] UserCreateDto dto)
    {
        await _userAppService.CreateUserAsync(dto);
        return CreatedAtAction(nameof(GetUser), new { id = dto.Id }, dto);
    }
}

5. 配置DI生命周期

在ASP.NET Core 5的Startup.cs中注册依赖,使用Scoped生命周期(对应每个请求一个实例,保证单一数据库连接):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
    services.AddScoped<IUnitOfWork, EfCoreUnitOfWork>();
    services.AddScoped<UserAppService>();
    
    services.AddControllers();
}

总结

通过DI将Unit of Work注入应用层,既符合Clean Architecture的依赖规则,又能保证单一数据库连接(Scoped生命周期对应请求周期),同时保持各层职责清晰、低耦合,也更易于测试和维护。

内容的提问来源于stack exchange,提问作者Eli A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:23:25