.NET Core 依赖注入 - 如何处理手动创建的实例?
Posted
技术标签:
【中文标题】.NET Core 依赖注入 - 如何处理手动创建的实例?【英文标题】:.NET Core dependency injection - How to dispose manually created instances? 【发布时间】: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 框架不会自动处理服务。
问题:
要让框架自动处理服务,有什么办法吗?
container.AddSingleton
由于我只是注册 func/factory 的定义而不是服务对象(如“BLogic”),AddSingleton 是否比使用下面的“AddScoped”或“AddTransisent”有任何优势?
container.AddTransisent<Func<string, IDBCache>>
(p=> (userId) => new IDBCache(userId, p.GetService<IBLogic>()));
【问题讨论】:
【参考方案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<IDBCache>()
),这使得它可以被容器正常处理。这将消除上述所有复杂性。
【讨论】:
以上是关于.NET Core 依赖注入 - 如何处理手动创建的实例?的主要内容,如果未能解决你的问题,请参考以下文章
MediatR CQRS - 如何处理不存在的资源(asp.net core web api)
ASP.NET Core MVC 502 bad gateway 超时如何处理
ASP.NET Core管道深度剖析:管道是如何处理HTTP请求的?