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

单例工厂存储的瞬态实例是否会表现为单例?该实现是否合理?

问题

我正在研读现有代码以理解依赖注入(DI)生命周期,现有代码中,注册为单例的PipelineFactory类会从字典中返回对应实例(若已存在),否则创建新的瞬态IPipeline实例并存入字典后返回。请问:

  1. 存储在该工厂内的这些瞬态实例是否会表现得像单例?
  2. 这种编码实现方式是否为良好的开发实践?

相关代码

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddHostedService<Consumer>()
    .AddSingleton<IPipelineFactory, PipelineFactory>()
    .AddTransient<IPipeline, Pipeline>();

var app = builder.Build();

app.MapGet("/", (IPipelineFactory pipelineFactory) =>
{
    return $"Total pipelines: {pipelineFactory.GetPipelineCount()}";
});


app.Run();

PipelineFactory实现

public class PipelineFactory : IPipelineFactory
{
    private readonly Dictionary<int, IPipeline> _pipelines = new();
    private readonly IServiceProvider _serviceProvider;

    public PipelineFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IPipeline GetPipeline(int pipelineId)
    {
        if (!_pipelines.ContainsKey(pipelineId))
        {
            using var scope = _serviceProvider.CreateScope();
            _pipelines.Add(pipelineId, scope.ServiceProvider.GetRequiredService<IPipeline>());                                
        }

        return _pipelines[pipelineId];
    }

    public int GetPipelineCount()
    {
        return _pipelines.Count;
    }
}
回答

1. 存储的瞬态实例是否表现得像单例?

是,这些IPipeline实例会完全表现出单例的行为:

  • 由于PipelineFactory是单例注册,它内部的_pipelines字典会在整个应用生命周期内持续存在。
  • 针对同一个pipelineId,第一次调用会创建IPipeline实例并存入字典,后续所有请求都会返回同一个实例,不会重新创建。
  • 哪怕IPipeline本身注册为瞬态,一旦被存入单例工厂的字典,就不会被GC回收(除非手动从字典移除),生命周期和单例完全一致。

2. 这种实现是否为良好的开发实践?

这种实现存在明显缺陷,不属于良好的开发实践,核心问题包括:

  • 内存泄漏风险:字典会永久持有所有创建过的IPipeline实例,直到应用关闭。如果pipelineId数量较多,会持续占用内存,无法被回收。
  • DI生命周期混乱:手动从容器获取实例并持有,绕过了DI容器的生命周期管理机制。如果IPipeline依赖了scoped类型的服务,会导致这些依赖服务无法被正确释放,引发资源泄漏或状态错误。
  • 线程安全隐患:Dictionary<TKey,TValue>不是线程安全集合,当多个并发请求同时调用GetPipeline创建新实例时,可能出现键重复添加、字典结构损坏等问题。
  • 可测试性差:工厂直接依赖IServiceProvider属于服务定位器模式,会增加单元测试的复杂度,无法方便地替换或模拟依赖。

优化建议

  • 如果确实需要按pipelineId复用实例,可以考虑使用按键注册的单例,或者实现带过期机制的缓存逻辑。
  • 若不需要长期复用,应每次获取时创建新的瞬态实例,避免在单例容器中持有实例引用。
  • 必须保证线程安全的话,改用ConcurrentDictionary<int, IPipeline>替代普通Dictionary。
  • 避免直接依赖IServiceProvider,通过构造函数注入Func<IPipeline>来创建瞬态实例,保持DI的清晰性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 01:29:50