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

为何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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:17:33