在 Autofac 中自动处理 child 个生命周期范围
Automatically dispose child lifetime scopes in Autofac
有很多次我在 Autofac 中创建一个 child 生命周期范围只是为了替换或补充 parent 中的注册。从那时起,我只使用 child 生命周期范围。 autofac 文档指出 child scopes are not automatically disposed:
Child Scopes are NOT Automatically Disposed
While lifetime scopes themselves implement IDisposable
, the lifetime scopes that you create are not automatically disposed for you. If you create a lifetime scope, you are responsible for calling Dispose()
on it to clean it up and trigger the automatic disposal of components.
然而,在这种情况下,我确实希望 children 被自动处理。
到目前为止我想到的解决方法是让 child 作用域处理 parent。像这样:
ILifetimeScope scope = ...;
var childScope = scope.BeginLifetimeScope();
childScope.Disposer.AddInstanceForDisposal(scope);
scope = childScope;
因为我从来没有跟踪过 scope
,只有 childScope
,所以我需要一种方法来处理 parent。
更复杂的是,每个 parent 可以有多个 child 作用域。所以在那些情况下我不能这样做。我只想在处理最后一个 child 时处理 parent。为此,我认为在 parent 中我必须在每次调用 BeginLifetimeScope()
时注册一些专用于引用计数的服务,并在处理 child 时减少该引用计数。
我不确定我是否正确地处理了这个问题,所以我想看看这里是否有更好的解决方案。我正在将我的代码库从 UnityContainer 迁移到 Autofac。该代码以前有一个 UnityDisposer
object 会从上到下遍历 parent/child 树并处理所有内容,但我没有使用 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
据我所知,对于线性 parent/child 关系,这似乎有效。但它并没有解决 multiple-child 问题。明确地说,我对线性情况的解决方案也不是特别满意。
从 Autofac 的角度来看,您实际上必须开始跟踪 parent 和 child 范围,以便您可以选择何时处置它们。这是一种 Spider-Man 情况:能力越大,责任越大。
有时这就像用 using
语句包装各种工作单元一样简单。在异步情况下,例如 ASP.NET,中间件和 HttpContext
等地方开始发挥作用。
将 parent 从 child 下移出不是个好主意。除了 parent 被处理后你无法真正解决 child 的问题(是的,我知道 parent 会以某种方式坚持到最后一个 child,但事实依然如此);我们肯定已经看到人们通过有意或无意地将 parent 从 child 下移除而陷入奇怪的边缘情况,并且根据它的连接方式和事情发生的顺序,你最终可能会导致涉及 ObjectDisposedException
.
的异常难以排除
如果您考虑要在 Autofac 上下文 之外 做什么,尝试让系统自行处理是一种糟糕的设计模式,例如让 child 范围以某种方式负责 parent。例如,如果您有一些其他 parent/child 关系想要执行此操作,您会经常看到某种处理引用计数和处置的外部协调器 - 在逻辑 parent/child 关系之外。
我建议改为做类似的事情 - 实际跟踪 parent 和 child 具有协调器 class 和某些性质的范围让协调器监视所有 child 范围消失。
此外,它会让其他情况变得更容易,比如“我创建了 parent,我创建了一个 child,它在我创建时被处理并清理了 parent试图创建第二个 child。”线程的东西。您将能够在您的协调程序代码中考虑到这一点。
有很多次我在 Autofac 中创建一个 child 生命周期范围只是为了替换或补充 parent 中的注册。从那时起,我只使用 child 生命周期范围。 autofac 文档指出 child scopes are not automatically disposed:
Child Scopes are NOT Automatically Disposed
While lifetime scopes themselves implement
IDisposable
, the lifetime scopes that you create are not automatically disposed for you. If you create a lifetime scope, you are responsible for callingDispose()
on it to clean it up and trigger the automatic disposal of components.
然而,在这种情况下,我确实希望 children 被自动处理。
到目前为止我想到的解决方法是让 child 作用域处理 parent。像这样:
ILifetimeScope scope = ...;
var childScope = scope.BeginLifetimeScope();
childScope.Disposer.AddInstanceForDisposal(scope);
scope = childScope;
因为我从来没有跟踪过 scope
,只有 childScope
,所以我需要一种方法来处理 parent。
更复杂的是,每个 parent 可以有多个 child 作用域。所以在那些情况下我不能这样做。我只想在处理最后一个 child 时处理 parent。为此,我认为在 parent 中我必须在每次调用 BeginLifetimeScope()
时注册一些专用于引用计数的服务,并在处理 child 时减少该引用计数。
我不确定我是否正确地处理了这个问题,所以我想看看这里是否有更好的解决方案。我正在将我的代码库从 UnityContainer 迁移到 Autofac。该代码以前有一个 UnityDisposer
object 会从上到下遍历 parent/child 树并处理所有内容,但我没有使用 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
据我所知,对于线性 parent/child 关系,这似乎有效。但它并没有解决 multiple-child 问题。明确地说,我对线性情况的解决方案也不是特别满意。
从 Autofac 的角度来看,您实际上必须开始跟踪 parent 和 child 范围,以便您可以选择何时处置它们。这是一种 Spider-Man 情况:能力越大,责任越大。
有时这就像用 using
语句包装各种工作单元一样简单。在异步情况下,例如 ASP.NET,中间件和 HttpContext
等地方开始发挥作用。
将 parent 从 child 下移出不是个好主意。除了 parent 被处理后你无法真正解决 child 的问题(是的,我知道 parent 会以某种方式坚持到最后一个 child,但事实依然如此);我们肯定已经看到人们通过有意或无意地将 parent 从 child 下移除而陷入奇怪的边缘情况,并且根据它的连接方式和事情发生的顺序,你最终可能会导致涉及 ObjectDisposedException
.
如果您考虑要在 Autofac 上下文 之外 做什么,尝试让系统自行处理是一种糟糕的设计模式,例如让 child 范围以某种方式负责 parent。例如,如果您有一些其他 parent/child 关系想要执行此操作,您会经常看到某种处理引用计数和处置的外部协调器 - 在逻辑 parent/child 关系之外。
我建议改为做类似的事情 - 实际跟踪 parent 和 child 具有协调器 class 和某些性质的范围让协调器监视所有 child 范围消失。
此外,它会让其他情况变得更容易,比如“我创建了 parent,我创建了一个 child,它在我创建时被处理并清理了 parent试图创建第二个 child。”线程的东西。您将能够在您的协调程序代码中考虑到这一点。