为何DI容器会两次释放Scoped服务?如何实现单次释放?
.NET DI Scoped服务重复释放问题分析与修复
服务定义
public interface IMessageWriter { void Write(string message); } public class ConsoleMessageWriter:IMessageWriter, IDisposable { public void Dispose() { Console.WriteLine($"The {GetType().Name} instance is disposed."); } public void Write(string message) { Console.WriteLine(message); } }
在作用域中使用该服务
using DI_Sandbox.Models; using Microsoft.Extensions.DependencyInjection; var services = new ServiceCollection(); services .AddScoped<ConsoleMessageWriter>() .AddScoped<IMessageWriter>(provider => provider.GetRequiredService<ConsoleMessageWriter>()); var container = services.BuildServiceProvider(validateScopes:true); using (var serviceScope = container.CreateScope()) { var messageWriter1 = serviceScope.ServiceProvider.GetRequiredService<IMessageWriter>(); var messageWriter2 = serviceScope.ServiceProvider.GetRequiredService<IMessageWriter>(); Console.WriteLine($"messageWriter1 == messageWriter2: {messageWriter1 == messageWriter2}"); messageWriter1.Write("Hello DI!"); } Console.Write("THE END");
运行输出
messageWriter1 == messageWriter2: True Hello DI! The ConsoleMessageWriter instance is disposed. The ConsoleMessageWriter instance is disposed. THE END
问题原因
你注册了两个独立的Scoped服务:ConsoleMessageWriter本身,以及IMessageWriter映射到ConsoleMessageWriter的实例。虽然这两个服务最终指向同一个对象,但.NET的DI容器会分别跟踪每个注册的服务条目。当作用域结束时,容器会遍历所有它负责跟踪的服务条目,尝试逐个释放——哪怕两个条目指向同一个对象,也会触发两次Dispose调用。
修复方案
方式一:直接注册接口到实现类(推荐)
不需要单独注册ConsoleMessageWriter,直接把IMessageWriter绑定到ConsoleMessageWriter,这样容器只会跟踪一个服务条目,释放时只会调用一次Dispose:
var services = new ServiceCollection(); services.AddScoped<IMessageWriter, ConsoleMessageWriter>();
方式二:保留双注册但避免重复释放
如果确实需要同时注册具体类型和接口(比如要直接获取ConsoleMessageWriter实例),可以通过代理类让容器不处理IMessageWriter实例的释放,把释放责任交给ConsoleMessageWriter的注册项:
var services = new ServiceCollection(); services.AddScoped<ConsoleMessageWriter>(); services.AddScoped<IMessageWriter>(provider => new NonDisposableWriterProxy(provider.GetRequiredService<ConsoleMessageWriter>())); // 辅助代理类,不实现IDisposable,避免容器触发释放 private class NonDisposableWriterProxy : IMessageWriter { private readonly ConsoleMessageWriter _innerWriter; public NonDisposableWriterProxy(ConsoleMessageWriter innerWriter) => _innerWriter = innerWriter; public void Write(string message) => _innerWriter.Write(message); }
内容的提问来源于stack exchange,提问作者Andrey Bushman
相关产品推荐
相关产品推荐

