Autofac:需要:在 AutofacModule 中处理的实例的生命周期结束事件

Autofac: Needed: End-Of-Lifetime event for instances handled in a AutofacModule

我使用 C# 和 Autofac 4.9.4。 我有一个连接到 IComponentRegistration.Activated 事件的 Autofac 模块。它查找某些 classes 的激活实例,并将它们注册到某个管理器 class 中。 当然,此注册应限于受影响对象的生命周期。所以模块需要知道对象何时被容器丢弃,然后从管理器中注销它。否则我会产生内存泄漏。 当我向 autofac ContainerBuilder 注册 class 时,有一个 OnRelease-Method,但那不是正确的地方;我在模块中需要这样的事件。

具体代码如下:

using Autofac;
using Autofac.Core;

namespace De.Gedat.Foundation.Bl.IoC
{

    public class ResetManagerModule : Module
    {
        protected override void AttachToComponentRegistration(
            IComponentRegistry componentRegistry,
            IComponentRegistration registration)
        {
            registration.Activated += (sender, e) =>
                RegisterToResetManager(e.Instance, e.Context);
            registration.Released += ???
        }

        private void RegisterToResetManager(object instance, IComponentContext context)
        {
            // Every IAutoRegisteredResettable object created by IoC will be picked up:
            var resettable = instance as IAutoRegisteredResettable;
            if (resettable == null)
                return;
            // Get the singleton IResetManager...
            var resetManager = context.Resolve<IResetManager>();
            // ...and register the instance with it:
            resetManager.RegisterInstance(resettable);

            // ...and on resettable's end-of-lifetime we would have to call:
            //resetManager.UnregisterInstance(resettable)
            //...but not at this point when the instance has just been created!
        }
    }
}

当对象被丢弃时,我如何才能注意到?

假设一个生命周期范围,我们希望让 autofac 为我们处理所有的处置,所以最好用 using 语句包装上下文,但它也会处置 resettable 我们赢了然后可以调用 resetManager.UnregisterInstance(resettable).

为了不处理您的 resettable 实例并在处理生命周期范围时仍然让 autofac 处理所有处理,我们可以告诉 autofac 我们想用 [=14 自己处理它=] (doc),并且在 finally 语句中我们可以调用 resetManager.UnregisterInstance(resettable) 然后手动处理实例。

private void RegisterToResetManager(object instance, IComponentContext context)
{
    var resettable = instance as IAutoRegisteredResettable;
    if (resettable == null)
        return;
    var resetManager = context.Resolve<IResetManager>();

    try
    {
        resetManager.RegisterInstance(resettable).ExternallyOwned();
    }
    finally
    {
        resetManager.UnregisterInstance(resettable);
        resettable.Dispose();
    }
}

我们想出了以下完美的解决方案:

public class ResetManagerAutofacModule : Module
{
    protected override void AttachToComponentRegistration(
        IComponentRegistryBuilder componentRegistryBuilder,
        IComponentRegistration registration)
    {
        registration.Activated += (sender, e) =>
            RegisterToResetManager(e.Instance, e.Context);
    }

    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<ResetManager>().As<IResetManager>().SingleInstance();
        // Because there are no module events for the end-of-lifetime of objects,
        // we use a disposable class as a helper to get informed about disposal
        // (declaration see below):
        builder
            .RegisterType<DisposeObserver>()
            .InstancePerDependency(); // important!!!
    }

    private void RegisterToResetManager(object instance, IComponentContext context)
    {
        var resettable = instance as IAutoRegisteredResettable;
        if (resettable == null)
            return;
        var resetManager = context.Resolve<IResetManager>();
        // Hook the object on the manager:
        resetManager.RegisterInstance(resettable);
        // Get a new instance of our dispose helper class from the container
        // which has the same lifetime as "instance" because DisposeObserver
        // is registered as InstancePerDependency.
        var disposableWithCallback = context.Resolve<DisposeObserver>();
        // When this helper is disposed, we know that the lifetime of "instance" is over.
        disposableWithCallback.DisposingCallback =
            // So we can unhook it from the manager:
            () => resetManager.UnregisterInstance(resettable);
    }

    public class DisposeObserver : IDisposable
    {
        public Action DisposingCallback { get; set; }
        public void Dispose()
        {
            DisposingCallback?.Invoke();
            DisposingCallback = null;
        }
    }
}