我使用 C# 和 Autofac 4.9.4。 我有一个连接到 IComponentRegistration.Activated 事件的 Autofac 模块。它查找某些类的激活实例,并在某个管理器类中注册它们。 当然,此注册应限制为受影响对象的生存期。因此,模块需要知道容器何时丢弃对象,然后从管理器中取消注册它。否则我会产生内存泄漏。 当我向autofac ContainerBuilder注册一个类时,有一个OnRelease方法,但这不是正确的地方;我需要模块中的这样一个事件。
具体代码如下所示:
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 我们希望使用ExternallyOwned
(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;
}
}
}