0

像下面的代码一样,我必须使用工厂/函数注册容器的方式,因为我提前不知道“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”,因为链接框架不会自动处理服务。

问题

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

  2. 容器。AddSingleton <Func<string, IDBCache>> (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>()));
4

1 回答 1

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为 Singleton 意味着创建IDBCache的实例只会在应用程序结束时被释放。如果您IDBCache在应用程序的生命周期内创建了许多实例,则会导致内存泄漏。在您的场景中,这不太可能是一个好主意。

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

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

如果您以在对象构造期间不需要运行时数据的IDBCache方式重新设计,您可以允许在没有工厂的情况下在容器中注册(例如),这允许它由容器正常处置。这将消除所有上面介绍的复杂性。IDBCacheAddTransient<IDBCache>()

于 2021-11-23T09:16:10.693 回答