I'm using the Microsoft Unity IOC container and I want to be able to raise an event every time a component is created (not registered) i.e. Lifetime events
For example
ComponentCreated
event.OnActivating
and OnRelease
events.RegisterInitializer
and Options.RegisterResolveInterceptor
methods.What is the equivalent in Unity?
Out of the box Unity does not have notifications. However, Unity provides the ability to extend the container functionality using a UnityContainerExtension
which would let you add notifications fairly easily.
There are a variety of stages where the container can hook into the build pipeline by specifying what part of the UnityBuildStage
you want the extension to run at (stages are Setup, TypeMapping, Lifetime, PreCreation, Creation, Initialization, PostInitialization). (It can get a bit tricky because the pipeline might short circuit as in the case of using the 'singleton' ContainerControlledLifetimeManager
where the pipeline stops at Lifetime.)
In a container extension there are already two events that notify of registrations: Registering
and RegisteringInstance
. Those could be useful if you care about registrations.
If you wanted to receive a notification when an object is resolved or torn down then you could write a container extension similar to this:
public class BuildNotificationExtension : UnityContainerExtension
{
public event EventHandler<NamedTypeBuildKey> BuiltUp;
public event EventHandler<object> TearDown;
protected override void Initialize()
{
var builderAwareStrategy = new CustomBuilderAwareStrategy(this.BuiltUp, this.TearDown);
// Run before Lifetime so we know about all resolved calls
// even if singleton that is not actually built up
base.Context.Strategies.Add(builderAwareStrategy, UnityBuildStage.TypeMapping);
}
private class CustomBuilderAwareStrategy : BuilderStrategy
{
private event EventHandler<NamedTypeBuildKey> BuiltUp;
private event EventHandler<object> TearDown;
public CustomBuilderAwareStrategy(EventHandler<NamedTypeBuildKey> builtUp,
EventHandler<object> tearDown)
{
this.BuiltUp = builtUp;
this.TearDown = tearDown;
}
public override void PreBuildUp(IBuilderContext context)
{
this.BuiltUp?.Invoke(this, context.BuildKey);
}
public override void PreTearDown(IBuilderContext context)
{
this.TearDown?.Invoke(this, context.Existing);
}
}
}
And if you ran this console app:
class Program
{
private class Foo {}
private interface ILogger { }
private class Logger : ILogger
{
public Logger(Foo foo1, Foo foo2) { }
}
static void Main(string[] args)
{
var buildNotificationExtension = new BuildNotificationExtension();
buildNotificationExtension.BuiltUp += (sender, key) =>
{
Console.WriteLine($"Built up type '{key.Type.Name}' with name '{key.Name}'.");
};
buildNotificationExtension.TearDown += (sender, obj) =>
{
Console.WriteLine($"Tear down type '{obj.GetType().Name}'.");
};
IUnityContainer container = new UnityContainer()
.AddExtension(buildNotificationExtension)
// Register ILogger as singleton
.RegisterType<ILogger, Logger>(new ContainerControlledLifetimeManager());
var logger = container.Resolve<ILogger>();
var anotherLogger = container.Resolve<ILogger>();
container.Teardown(logger);
}
}
You would receive the following output:
Built up type 'Logger' with name ''.
Built up type 'Foo' with name ''.
Built up type 'Foo' with name ''.
Built up type 'Logger' with name ''.
Tear down type 'Logger'.
Showing that first Logger
was resolved which caused two Foo
instances to be resolved. Then Logger
was resolved again but since it's a singleton the current Logger
instance was returned and no new Foo
instances were instantiated. Finally the Logger
was torn down.
This approach can be used to add additional notifications to different stages of Unity processing pipeline. e.g. you could modify the BuildNotificationExtension
class to accept multiple stages and add notifications to each stage.