【问题标题】:.NET Core dependency injection - How to dispose manually created instances?.NET Core 依赖注入 - 如何处理手动创建的实例?
【发布时间】:2022-01-01 14:54:21
【问题描述】:

像下面的代码一样,我必须使用工厂/函数注册容器的方式,因为我不知道“userId”属性的值提前但只在运行时。这是有效的,功能方面没有问题。

container.AddSingleton<IBLogic, BLogic>(); //explicitly registration 'BLogic' service object

container.AddSingleton<Func<string, IDBCache>>
        (p=> (userId) => new IDBCache(userId, p.GetService<IBLogic>()));

由于这里我使用的是“new IDBCache”,根据link 框架不会自动处理服务。

问题

  1. 要让框架自动处理服务,有什么办法吗?

  2. container.AddSingleton> (p=> (userId) => new IDBCache(userId, p.GetService()));

由于我只是注册 func/factory 的定义而不是服务对象(如“BLogic”),AddSingleton 是否比使用下面的“AddScoped”或“AddTransisent”有任何优势?

container.AddTransisent<Func<string, IDBCache>>
        (p=> (userId) => new IDBCache(userId, p.GetService<IBLogic>()));

【问题讨论】:

    标签: c# dependency-injection asp.net-core-webapi asp.net-core-3.1 servicecollection


    【解决方案1】:

    对此没有简单的解决方法。 MS.DI 不包含您可以调用的RegisterForDisposal 方法。您将必须创建一个单独的包装器来实现一次性的,您可以将创建的实例挂接到该包装器上。例如:

    services.AddSingleton<IBLogic, BLogic>();
    
    services.AddScoped<DisposeWrapper>();
    services.AddScoped<Func<string, IDBCache>>(p => (userId) =>
    {
        var cache = new IDBCache(userId, p.GetRequiredService<IBLogic>());
        p.GetRequiredService<DisposeWrapper>().Add(cache);
        return cache;
    });
    

    DisposeWrapper 如下所示:

    public sealed class DisposeWrapper : List<IDisposable>, IDisposable
    {
        public void Dispose()
        {
            // Dispose in reverse order as creation.
            for (int i = this.Count - 1; i >= 0; i--) this[i].Dispose();
        }
    }
    

    然而,这样的实现带有相当多的警告,因为DisposeWrapper 应该使用与您想要缓存IDBCache 相同的范围注册。这就是为什么在上面的示例中,我将DisposeWrapper 和工厂都注册为Scoped

    DisposeWrapper 注册为单例,意味着创建的IDBCache 实例只会在应用程序结束时被释放。如果您在应用程序的生命周期内创建了许多IDBCache 实例,则会导致内存泄漏。在您的场景中,这不太可能是个好主意。

    另一方面,将两者都注册为瞬态仍然可能(令人困惑地)导致内存泄漏,因为工厂可能被注入到单例中,导致它(及其DisposeWrapper)变成Captive Dependency

    由于这种设计的复杂性,我建议更改您的设计。

    如果你以runtime data isn't required during object construction的方式重新设计IDBCache,你可以允许IDBCache在没有工厂的容器中注册(例如AddTransient&lt;IDBCache&gt;()),这使得它可以被容器正常处理。这将消除上述所有复杂性。

    【讨论】:

      猜你喜欢
      • 2017-04-12
      • 1970-01-01
      • 1970-01-01
      • 2022-01-27
      • 2016-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多