依赖注入中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
相关产品推荐
相关产品推荐

