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

瞬态IDisposable服务内存泄漏:寻求无需修改消费者的解决方案

解决瞬态IDisposable服务内存泄漏(无需修改消费者代码)

问题根源:从根服务提供者获取瞬态IDisposable服务时,.NET DI容器会自动跟踪实例,哪怕你用using手动Dispose,根容器依然持有引用,直到自身被释放,这就导致了内存泄漏。

下面是两种不用改现有消费者代码的解决方案:

方案一:.NET 8+ 禁用容器跟踪(最优)

直接修改注入代码,注册服务时关掉容器对该实例的跟踪:

public static IServiceCollection AddUnitOfWork(this IServiceCollection services)
{
    var descriptor = ServiceDescriptor.Transient<IUnitOfWork>(provider => 
        new UnitOfWork(provider.GetRequiredService<IConfiguration>()));
    
    // 告诉容器别跟踪这个实例的生命周期
    descriptor.SetIsDisposableTrackingEnabled(false);
    
    services.Add(descriptor);
    return services;
}

这样一来,实例的释放完全由你的using块负责,根容器不会再攥着引用不放,直接解决泄漏问题。

方案二:包装类自动管理服务范围(兼容.NET 6及更早版本)

如果还没升级到.NET 8,可以用包装类自动创建服务范围,让实例由范围容器跟踪,而非根容器:

第一步:写个包装类

public class ScopedUnitOfWorkWrapper : IUnitOfWork
{
    private readonly IUnitOfWork _innerUow;
    private readonly IServiceScope _scope;

    public ScopedUnitOfWorkWrapper(IServiceProvider rootProvider)
    {
        // 每次获取服务时创建新的范围
        _scope = rootProvider.CreateScope();
        // 从范围里拿真实的UnitOfWork实例
        _innerUow = _scope.ServiceProvider.GetRequiredService<UnitOfWork>();
    }

    public void Dispose()
    {
        // 先释放内部实例,再销毁范围(范围会自动清理所有跟踪的对象)
        _innerUow.Dispose();
        _scope.Dispose();
    }

    public ValueTask DisposeAsync()
    {
        try
        {
            return _innerUow.DisposeAsync();
        }
        finally
        {
            _scope.Dispose();
        }
    }

    // 把IUnitOfWork里的其他方法都委托给_innerUow
    // 比如你的IUnitOfWork有SaveChangesAsync,就加:
    // public Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    // {
    //     return _innerUow.SaveChangesAsync(cancellationToken);
    // }
}

第二步:修改注入代码

public static IServiceCollection AddUnitOfWork(this IServiceCollection services)
{
    // 先注册真实的UnitOfWork实现(不让消费者直接拿)
    services.AddTransient<UnitOfWork>();
    // 注册包装类作为IUnitOfWork的对外实现
    services.AddTransient<IUnitOfWork>(provider => 
        new ScopedUnitOfWorkWrapper(provider));
    return services;
}

原理:消费者拿到的是包装类,它会自动创建独立的服务范围,真实实例由范围容器跟踪。当using块结束调用Dispose时,包装类会释放内部实例和范围,范围容器会立刻清理所有跟踪的对象,根容器不会留任何引用,也就不会泄漏了。

内容的提问来源于stack exchange,提问作者Niccolò Pecora

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 15:14:59