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

C#依赖注入:单例类依赖Scoped服务启动报错但同配置应用正常

单例类依赖Scoped服务的DI冲突问题解决

问题背景

通过AddSingleton注册的CacheRepository单例类,其构造函数依赖IRequestClient<ICacheRepository>,而该服务由busConfigurator.AddRequestClient()注册为Scoped生命周期。启动应用时触发DI生命周期错误:

Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'

但另一套配置完全相同的应用却能正常运行,且业务要求CacheRepository必须保持单例(用于缓存大量数据库数据,改为Transient会重复加载数据)。

核心矛盾与可能原因

矛盾本质

DI容器的生命周期规则明确:单例服务不能直接依赖Scoped服务,因为Scoped服务的生命周期绑定到请求/范围,单例创建时Scoped服务尚未存在,或后续无法正确回收。

另一应用正常的可能原因

  1. MassTransit版本差异:不同版本的AddRequestClient默认生命周期不同,旧版本可能将IRequestClient注册为Singleton而非Scoped。
  2. DI容器行为差异:另一应用使用的DI容器(如Autofac)默认未启用生命周期检查,允许这种不安全的依赖。
  3. 实际依赖路径不同:另一应用的CacheRepository并非直接注入IRequestClient,而是通过工厂模式/IServiceProvider间接获取,规避了直接依赖。
  4. 注册顺序差异:虽然代码看似相同,但服务注册顺序可能导致DI容器的解析逻辑变化(概率较低)。

解决方案

要在保持CacheRepository单例的同时解决依赖冲突,需通过间接获取Scoped服务的方式,而非直接注入。以下是两种可行方案:

方案1:通过IServiceProvider创建临时Scope获取IRequestClient

修改CacheRepository的构造函数,注入IServiceProvider,在需要使用IRequestClient时创建临时Scope:

public class CacheRepository : ICacheRepository
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<CacheRepository> _logger;
    private object _cachedData; // 缓存的数据对象

    public CacheRepository(IServiceProvider serviceProvider, ILogger<CacheRepository> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }

    public async Task RefreshCache()
    {
        // 创建临时Scope,确保Scoped服务的生命周期正确管理
        using var scope = _serviceProvider.CreateScope();
        var requestClient = scope.ServiceProvider.GetRequiredService<IRequestClient<ICacheRepository>>();
        
        // 调用请求客户端获取数据并缓存
        var response = await requestClient.GetResponse<CacheDataResponse>(new CacheDataRequest());
        _cachedData = response.Message.Data;
        
        _logger.LogInformation("缓存已更新");
    }
}

方案2:使用MassTransit的IRequestClientFactory

MassTransit提供了IRequestClientFactory用于创建请求客户端,该工厂本身是Singleton生命周期,可安全注入到单例服务中:

public class CacheRepository : ICacheRepository
{
    private readonly IRequestClientFactory _clientFactory;
    private readonly ILogger<CacheRepository> _logger;
    private object _cachedData;

    public CacheRepository(IRequestClientFactory clientFactory, ILogger<CacheRepository> logger)
    {
        _clientFactory = clientFactory;
        _logger = logger;
    }

    public async Task RefreshCache()
    {
        // 通过工厂创建请求客户端,自动处理生命周期
        using var requestClient = _clientFactory.CreateClient<IRequestClient<ICacheRepository>>();
        var response = await requestClient.GetResponse<CacheDataResponse>(new CacheDataRequest());
        _cachedData = response.Message.Data;
        
        _logger.LogInformation("缓存已更新");
    }
}

额外建议

  • 检查另一应用的MassTransit版本,对比AddRequestClient的注册逻辑,确认是否存在版本差异导致的生命周期不同。
  • 避免强制修改IRequestClient的生命周期为Singleton,这会破坏MassTransit的设计,可能引发线程安全或资源泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:50:23