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

不使用服务定位器,从两个作用域使用作用域服务(如DbContext)

独立DbContext工作单元的无DI框架依赖实现

核心需求回顾

  • 执行两个独立工作单元,第一个失败回滚后第二个必须正常执行
  • 通过依赖注入提供DbContext,禁止手动创建或使用服务定位器
  • 服务尽可能减少对DI框架具体类型的依赖,同时保持依赖关系清晰可查

解决方案:抽象作用域工厂 + 独立工作单元

通过定义自定义抽象接口封装作用域创建逻辑,让业务服务只依赖抽象接口而非DI框架类型,同时将每个工作单元封装为独立服务,明确依赖关系。

1. 定义抽象作用域接口

先定义两个核心抽象:负责创建作用域的工厂,以及作用域本身

// 抽象作用域工厂:用于创建独立工作单元的作用域
public interface IUnitOfWorkScopeFactory
{
    IUnitOfWorkScope CreateScope();
}

// 抽象作用域:提供在当前作用域内解析服务的能力
public interface IUnitOfWorkScope : IDisposable
{
    T Resolve<T>() where T : notnull;
}

2. 基于DI框架实现抽象接口(以ASP.NET Core为例)

这部分是唯一依赖DI框架的代码,隔离在单独实现类中,业务服务无需感知

// 实现作用域工厂:封装ASP.NET Core的IServiceScopeFactory
public class AspNetCoreUnitOfWorkScopeFactory : IUnitOfWorkScopeFactory
{
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public AspNetCoreUnitOfWorkScopeFactory(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }

    public IUnitOfWorkScope CreateScope()
    {
        return new AspNetCoreUnitOfWorkScope(_serviceScopeFactory.CreateScope());
    }
}

// 实现作用域:封装ASP.NET Core的IServiceScope
public class AspNetCoreUnitOfWorkScope : IUnitOfWorkScope
{
    private readonly IServiceScope _scope;

    public AspNetCoreUnitOfWorkScope(IServiceScope scope)
    {
        _scope = scope;
    }

    public T Resolve<T>() where T : notnull
    {
        return _scope.ServiceProvider.GetRequiredService<T>();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

3. 注册服务到DI容器

在Startup/Program.cs中完成服务注册:

// 注册自定义作用域工厂
services.AddScoped<IUnitOfWorkScopeFactory, AspNetCoreUnitOfWorkScopeFactory>();

// 注册DbContext(默认Scoped生命周期,符合独立工作单元需求)
services.AddDbContext<MyDbContext>(options => 
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

// 注册工作单元服务
services.AddScoped<IFirstWorkUnit, FirstWorkUnit>();
services.AddScoped<ISecondWorkUnit, SecondWorkUnit>();

4. 封装独立工作单元

每个工作单元专注于自身业务逻辑,直接注入DbContext,无需关心作用域:

// 第一个工作单元接口
public interface IFirstWorkUnit
{
    Task<bool> ExecuteAsync();
}

// 第一个工作单元实现
public class FirstWorkUnit : IFirstWorkUnit
{
    private readonly MyDbContext _dbContext;

    public FirstWorkUnit(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<bool> ExecuteAsync()
    {
        try
        {
            // 执行第一个工作单元的业务操作
            _dbContext.Orders.Add(new Order { /* 订单数据 */ });
            await _dbContext.SaveChangesAsync();
            return true;
        }
        catch
        {
            // 无需手动回滚:作用域Dispose后DbContext会被释放,未提交的变更自动丢弃
            return false;
        }
    }
}

// 第二个工作单元接口
public interface ISecondWorkUnit
{
    Task ExecuteAsync();
}

// 第二个工作单元实现
public class SecondWorkUnit : ISecondWorkUnit
{
    private readonly MyDbContext _dbContext;

    public SecondWorkUnit(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task ExecuteAsync()
    {
        // 执行第二个工作单元的业务操作(不受第一个单元失败影响)
        _dbContext.OperationLogs.Add(new OperationLog 
        { 
            Message = "第一个工作单元执行完成(成功/失败)",
            Timestamp = DateTime.UtcNow
        });
        await _dbContext.SaveChangesAsync();
    }
}

5. 业务服务实现

业务服务仅依赖自定义抽象接口,依赖关系清晰,完全脱离DI框架具体类型:

public class OrderProcessingService
{
    private readonly IUnitOfWorkScopeFactory _scopeFactory;

    // 服务签名明确体现依赖:需要创建独立工作单元的能力
    public OrderProcessingService(IUnitOfWorkScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task ProcessOrderAsync()
    {
        bool firstUnitSuccess = false;

        // 处理第一个工作单元:独立作用域保证DbContext隔离
        using (var firstScope = _scopeFactory.CreateScope())
        {
            var firstWorkUnit = firstScope.Resolve<IFirstWorkUnit>();
            firstUnitSuccess = await firstWorkUnit.ExecuteAsync();
        }

        // 处理第二个工作单元:无论第一个是否成功,都执行
        using (var secondScope = _scopeFactory.CreateScope())
        {
            var secondWorkUnit = secondScope.Resolve<ISecondWorkUnit>();
            await secondWorkUnit.ExecuteAsync();
        }
    }
}

方案优势

  1. 依赖关系清晰:服务签名明确展示所需依赖(IUnitOfWorkScopeFactory),而非模糊的DI框架类型
  2. 低DI框架耦合:业务服务完全不依赖ASP.NET Core的IServiceScopeFactory,更换DI框架仅需重新实现抽象接口
  3. 工作单元隔离:每个工作单元在独立作用域内获取DbContext,第一个单元的变更不会污染第二个单元
  4. 单一职责原则:工作单元专注于业务逻辑,作用域工厂专注于作用域创建,职责划分清晰

对比不推荐的实现(注入IServiceScopeFactory)

// 不推荐的方案示例
public class BadOrderProcessingService
{
    private readonly IServiceScopeFactory _scopeFactory;

    // 问题1:依赖DI框架具体类型,耦合度高
    // 问题2:服务签名无法体现需要IFirstWorkUnit和ISecondWorkUnit,依赖不透明
    public BadOrderProcessingService(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task ProcessOrderAsync()
    {
        using (var scope = _scopeFactory.CreateScope())
        {
            var firstWorkUnit = scope.ServiceProvider.GetRequiredService<IFirstWorkUnit>();
            await firstWorkUnit.ExecuteAsync();
        }

        using (var scope = _scopeFactory.CreateScope())
        {
            var secondWorkUnit = scope.ServiceProvider.GetRequiredService<ISecondWorkUnit>();
            await secondWorkUnit.ExecuteAsync();
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:47:27