I use C# and Autofac 4.9.4. I have an Autofac Module which hooks up to the IComponentRegistration.Activated event. It looks for activated instances of certain classes and registers them in some manager class. This registration should be limited to the lifetime of the affected objects, of course. So the module needs to know when the object is discarded by the container and then unregister it from the manager. Otherwise I would produce a memory leak. There is a OnRelease-Method when I register a class with the autofac ContainerBuilder, but that is not the right place; I need such an event within the module.
The concrete code looks something like this:
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!
}
}
}
How can I get noticed when an object is discarded?
We came up with the following solution which works perfectly:
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;
}
}
}