【发布时间】:2021-10-27 18:12:21
【问题描述】:
有很多次我在 Autofac 中创建子生命周期范围只是为了替换或补充父项中的注册。从那时起,我只使用子生命周期范围。 autofac 文档指出child scopes are not automatically disposed:
子范围不会自动处理
虽然生命周期范围本身实现
IDisposable,但您创建的生命周期范围不会自动为您处置。如果你创建了一个生命周期范围,你有责任在它上面调用Dispose()来清理它并触发组件的自动处理。
但是,在这种情况下,我确实希望自动处置孩子。
到目前为止,我想到的解决方法是让子范围处置父范围。像这样的:
ILifetimeScope scope = ...;
var childScope = scope.BeginLifetimeScope();
childScope.Disposer.AddInstanceForDisposal(scope);
scope = childScope;
由于我从未跟踪过scope,只有childScope,我需要一种方法来处置父级。
更复杂的是,每个父级都可以有多个子域。所以在这些情况下我不能这样做。我只想在处置最后一个孩子时处置父母。为此,我认为每次调用 BeginLifetimeScope() 时,我都必须在父级中注册一些专用于引用计数的服务,并在处置子级时减少该引用计数。
我不确定我是否正确地处理了这个问题,所以我想看看这里是否有更好的解决方案。我正在将我的代码库从 UnityContainer 迁移到 Autofac。之前的代码有一个 UnityDisposer 对象,它会从上到下遍历父/子树并处理所有内容,但我没有使用 Autofac 获得那种层次结构。
编辑
对我推测的解决方案有一些疑问,所以我编写了一个示例应用程序来看看会发生什么:
class ThingA : IDisposable
{
public ThingA()
{
Console.WriteLine("Construct ThingA");
}
public void Dispose()
{
Console.WriteLine("Dispose ThingA");
}
}
class ThingB : IDisposable
{
public ThingB()
{
Console.WriteLine("Construct ThingB");
}
public void Dispose()
{
Console.WriteLine("Dispose ThingB");
}
}
public static class Program
{
public static void Main()
{
ContainerBuilder builder;
builder = new ContainerBuilder();
builder.RegisterType<ThingA>().InstancePerLifetimeScope();
ILifetimeScope container1 = builder.Build();
container1.Resolve<ThingA>();
var container2 = container1.BeginLifetimeScope(builder2 =>
{
builder2.RegisterType<ThingB>().InstancePerLifetimeScope();
});
container2.Disposer.AddInstanceForDisposal(container1);
container1 = container2;
container1.Resolve<ThingB>();
container1.Dispose();
}
}
我得到的输出是:
Construct ThingA
Construct ThingB
Dispose ThingB
Dispose ThingA
据我所知,对于线性父/子关系,这似乎可行。但这并不能解决多子问题。并且明确地说,我对线性情况的解决方案也不是特别满意。
【问题讨论】:
-
我想知道这里是否存在冲突 - 当孩子活跃时处置父母是否有意义?那安全吗?
-
@DanielA.White 我也考虑过这一点,但由于父母不处置其子生命周期范围,我认为这使它安全。无论如何,您的问题与我的问题完全相同,也是本文的目的。
-
@void.pointer 为什么要更改变量
scope?为什么不让它成为父母并与childScope一起工作呢?但更重要的是:为什么不直接打电话给scope.Disposer.AddInstanceForDisposal(childScope);?这样子作用域就会在父作用域被释放时被释放。 -
因为我需要使用子范围来访问它的注册。父范围仅用于一个目的:提供注册的基本子集。我的代码库只对只能从孩子访问的超集感兴趣。而且因为我对跟踪
parent不感兴趣,这意味着我需要孩子确保它被处置。 -
@void.pointer 什么时候应该处置孩子?