我试图更好地理解IoC/DI,特别是作用域的概念。
我正在使用Pluralsight course(很棒的服务,顺便说一句)中的一个示例WPF应用程序(repo here),并对其进行了轻微的修改。
以下是original code:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
oMainView = oContainer.Resolve(Of MainView)
oMainView.Show()
End Sub
......我把它改成了:
Private Sub App_Startup(Sender As App, e As StartupEventArgs) Handles Me.Startup
Dim oBootStrapper As BootStrapper
Dim oContainer As IContainer
Dim oMainView As MainView
oBootStrapper = New BootStrapper
oContainer = oBootStrapper.GetContainer
Using oScope As ILifetimeScope = oContainer.BeginLifetimeScope
oMainView = oScope.Resolve(Of MainView)
oMainView.Show()
End Using
End Sub
但是,当我尝试添加新朋友时,这会导致ObjectDisposedException
:
无法解析示例,也无法从此LifetimeScope创建嵌套生存期,因为它(或其父范围之一)已被释放。
这一切似乎与我在官方文件中发现的内容相矛盾:
务必始终从生存期范围而非根容器解析服务。
这一指导使我首先引入了作用域。但是,如果作用域部署了应用程序运行所必需的对象,那么我们如何希望使用它呢?此外,我们为什么要使用它呢?
我糊涂了。我提议的用法不对吗?
- --编辑--*
澄清一下,我正在寻找官方指南所提出的难题的解决方案:如果我们坚持这一点,我们的应用程序将失败。但如果我们不这样做,我们的风险内存泄漏(根据文档)。
怎么办?
1条答案
按热度按时间f0brbegy1#
如果你没有考虑到这一点,比如说,有一个长时间运行的应用偶尔会处理数据库--这些工作可能会周期性地从容器中解析一个可释放的数据库上下文,即使调用代码执行dispose,容器也会挂起它:内存泄漏。
然而,把它和关于单例的文档放在一起。单例,即使你从一个子作用域解析它们,* 仍然存在于容器根 *。为什么?因为如果你让一个作用域处理一个共享的单例,那么对于其他需要单例的东西来说,这将是一个问题。
"从作用域解决问题"指导是为了防止人们搬起石头砸自己的脚,因为他们没有意识到他们只使用容器就引入了内存泄漏。
如果您正在解析的东西不是可释放的,并且在整个依赖链中没有任何东西是可释放的,那么您 * 可能 * 会安全地从容器中获取它。
这里更大的教训是要努力与您的生存期范围和您希望如何处置处理。我不是一个WPF的家伙,但如果应用程序的主窗口需要解决,并需要生存的整个应用程序的持续时间,看起来这是单例的完美用法......它不会在生存期结束时被处理,您也不会看到异常。Autofac也有一些方法可以选择不让它进行处置,这会阻止它保存那些引用。
但是很难说"总是做这个"和"从不做那个",因为正如你所看到的,生存期范围和处置是一个复杂的主题。深入研究 * 为什么 * 是很重要的,内存管理和处置就是原因。出于同样的原因,正确获得注册组件的生存期也同样重要。