1

Closed

Unity not respecting HierarchicalLifetimeManager when manually registering IServiceLocator

description

I'm seeing some unexpected behaviour in Unity when registering and resolving IServiceLocator using the HierarchicalLifetimeManager.

What I'm seeing suggests that Unity treats the UnityServiceLocator as a static singleton, even when it is configured otherwise. This behaviour seems to be particular to UnityServiceLocator.

Background: I am slowly phasing out the "static" ServiceLocator in a few places in our code where it exists. The first step was going to be to register IServiceLocator in Unity and then have it injected as a dependency into those classes that use it.

In the following code, I register an injection factory to create a new instance of UnityServiceLocator. I also scope it with HierarchicalLifetimeManager to give me one instance per child container:
private static void Main(string[] args)
{
    var container = new UnityContainer();
    container.RegisterType<IServiceLocator>(
        new HierarchicalLifetimeManager(),
        new InjectionFactory(
            c =>
                {
                    Console.WriteLine("InjectionFactory invoked with container: " + c.GetHashCode());
                    return new UnityServiceLocator(c);
                }));

    container.Resolve<IServiceLocator>(); // expect to see console output here
    container.Resolve<IServiceLocator>(); // don't expect to see console output here

    var child = container.CreateChildContainer();
    child.Resolve<IServiceLocator>(); // expect to see console output here
    container.Resolve<IServiceLocator>(); // don't expect to see console output here

    var anotherChildContainer = container.CreateChildContainer();
    anotherChildContainer.Resolve<IServiceLocator>(); // expect to see console output here
    anotherChildContainer.Resolve<IServiceLocator>(); // don't expect to see console output here
}
I would expect the above code to invoke the factory, create the UnityServiceLocator instance and write out console output three times, once for each container. It doesn't - it does it once, as if I'd registered it as a singleton:

InjectionFactory invoked with container: 20903718

It gets worse:

If I now make my own class implement IServiceLocator (literally, implement the interface, one ctor which takes IUnityContainer, and have all methods throw NotImplementedException), and swap the line
 return new UnityServiceLocator(c);
with
 return new MyUnityServiceLocator(c);
This starts behaving the way I'd expect:

InjectionFactory invoked with container: 20903718
InjectionFactory invoked with container: 51746094
InjectionFactory invoked with container: 41215084

I just cannot understand this behaviour unless Unity treats UnityServiceLocator as a special case.

This feels like a bug to me, or possibly some undocumented special behaviour.

Can someone confirm if this is a bug or by design?
Closed Mar 13, 2014 at 7:17 PM by gmelnik
Guidance is provided in the comments.

comments

randylevy wrote Mar 16, 2013 at 7:35 AM

The reason why you are seeing the above behavior is that UnityServiceLocator registers itself in the container with an ExternallyControlledLifetimeManager lifetime:
public UnityServiceLocator(IUnityContainer container)
{
    this.container = container;
    container.RegisterInstance<IServiceLocator>(this, new ExternallyControlledLifetimeManager());
}
I would say that this is done by design. As you say if you need a different behavior, then you could create your own locator or potentially override the original lifetime manager registration with another.
new InjectionFactory(
    c =>
    {
        Console.WriteLine("InjectionFactory invoked with container: " + c.GetHashCode());
        var locator = new UnityServiceLocator(c);
        c.RegisterInstance<IServiceLocator>(locator, new HierarchicalLifetimeManager());
        return locator;
    })
~~
Randy Levy
entlib.support@live.com
Enterprise Library support engineer
Support How-to

rob_levine wrote Mar 19, 2013 at 9:44 AM

Thanks Randy, this explains everything. I've gone down the route of implementing IServiceLocator myself, although I think your suggestion to override the registration should work too.