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

依赖注入中IDisposable对象的生命周期管理方案选型

重构Job类时的IDisposable生命周期管理问题

我正在重构一个遗留的Job类以适配依赖注入模式,但在管理IDisposable对象的生命周期时遇到了困难,特此向社区求助。

原类实现

public class Job
{
    public void Execute()
    {
        /// Logic1, Logic2, DataProvider, DataProvider2 all have different dependencies
        /// Ignore to limit the scope of this question 
        /// first unit of work, taking ~5 minutes due to complex math calc and data size
        var first = new Logic1();
        var data1 = new DataProvider().Get();
        using (StreamWriter sw = new StreamWriter (new FileStream(new Config1().filePath, open)))
        {
            foreach (data in data1)
            {
                var result = first.Compute(data);
                sw.write(result);
            }
        }

        /// second unit of work, taking ~20 minutes due to complex math calc and data size
        var second = new Logic2();
        var data2 = new DataProvider2().Get();
        using (StreamWriter sw = new StreamWriter(new FileStream(new Config2().filePath, open)))
        {
            foreach (data in data2)
            {
                var result = second.Compute(data);
                sw.write(result);
            }
        }
    }
}

初步重构后的代码

public class Job
{
    private readonly IWork1 _work1;
    private readonly IWork2 _work2;

    public Job(IWork1 work1, IWork2 Work2)
    {
        _work1 = work1;
        _work2 = work2;
    }

    public void Execute()
    {
        _work1.DoWork();
        // work2 must happen after work1, cannot be in parallel
        _work2.DoWork2();
    }
}


internal class Work1 : IWork1
{
    private readonly ILogic1 _logic;
    private readonly IDataProvider _dataProvider;
    private readonly IWriter1 _writer;

    public Work1(ILogic1 logic, IDataProvider dataProvider, IWriter1 writer){
        _logic = logic;
        _dataProvider = dataProvider;
        _writer = writer;
    }

    internal void DoWork()
    {
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

internal class Work2 : IWork2
{
    private readonly ILogic2 _logic;
    private readonly IDataProvider2 _dataProvider;
    private readonly IWriter2 _writer;

    public Work2(ILogic2 logic, IDataProvider2 dataProvider, IWriter2 writer){
        _logic = logic;
        _dataProvider = dataProvider;
        _writer = writer;
    }

    internal void DoWork2()
    {
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

该方案看似可行,但存在一个问题:Job在一个作用域内执行,IWriter1和IWriter2的具体实现Writer1、Writer2均实现了IDisposable(用于保持文件流,避免百万次开闭操作),导致Writer1要等到Work2完成后才会被释放,不必要占用资源长达20分钟。

引入IWriterFactory的重构方案

受IDbContextFactory启发,我引入IWriterFactory进一步重构:

internal class Work1 : IWork1
{
    private readonly ILogic1 _logic;
    private readonly IDataProvider _dataProvider;
    private readonly IWriterFactory _factory;

    public Work1(ILogic1 logic, IDataProvider dataProvider, IWriterFactory factory){
        _logic = logic;
        _dataProvider = dataProvider;
        _factory = factory;
    }

    internal void DoWork()
    {
        using (var writer = _factory.GetWriter1())
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

internal class Work2 : IWork2
{
    private readonly ILogic2 _logic;
    private readonly IDataProvider2 _dataProvider;
    private readonly IWriterFactory _factory;

    public Work2(ILogic2 logic, IDataProvider2 dataProvider, IWriterFactory factory){
        _logic = logic;
        _dataProvider = dataProvider;
        _factory = factory;
    }

    internal void DoWork2()
    {
        using (var writer = _factory.GetWriter2())
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

该方案解决了生命周期问题,但将IWriter1和IWriter2的所有权从IWriterFactory转移到了Work1和Work2,需要消费者显式管理生命周期。

基于独立作用域的重构方案

为避免让Work类控制Writer的生命周期,我再次修改代码,为每个Work添加独立作用域:

public class Job
{
    private readonly IServiceProvider _services;

    public Job(IServiceProvider services)
    {
        _services = services;
    }

    public void Execute()
    {
        using (var scope1 = _services.CreateScope())
        {
            IWork1 work = scope1.ServiceProvider.GetRequired<IWork1>();
            work.DoWork();
        }

        using (var scope2 = _services.CreateScope())
        {
            IWork2 work = scope2.ServiceProvider.GetRequired<IWork2>();
            work.DoWork2();
        }
    }
}


internal class Work1 : IWork1
{
    private readonly ILogic1 _logic;
    private readonly IDataProvider _dataProvider;
    private readonly IWriter1 _writer;

    public Work1(ILogic1 logic, IDataProvider dataProvider, IWriter1 writer){
        _logic = logic;
        _dataProvider = dataProvider;
        _writer = writer;
    }

    internal void DoWork()
    {
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

internal class Work2 : IWork2
{
    private readonly ILogic2 _logic;
    private readonly IDataProvider2 _dataProvider;
    private readonly IWriter2 _writer;

    public Work2(ILogic2 logic, IDataProvider2 dataProvider, IWriter2 writer){
        _logic = logic;
        _dataProvider = dataProvider;
        _writer = writer;
    }

    internal void DoWork2()
    {
        foreach (data in _dataProvider.Get())
        {
            var result = _logic.Compute(data);
            writer.write(result);
        }
    }
}

此时Writer可以是任意类型,Work无需知晓其实现了IDisposable,但这明显是服务定位器模式,我不确定这是否为理想方案。

问题

若要求Writer1和Writer2在不再需要时立即释放,工厂模式与服务定位器模式哪种更优?是否有更好的解决方案?

更新Job使用场景

Job类位于独立库中,供不同应用宿主使用,提供如下扩展方法:

public static class JobDI
{
    public static void AddJob(this IServiceCollection services)
    {
        // register other internal services
        services.AddScoped<Job>();
    }
}

在各应用的Program.cs中使用方式如下:

public class Program
{
    public static async Task Main(string[] arg)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddJob();

        /// other codes
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 19:17:31