RoBlog – Refactoring Reality

Discrete thought packets on .Net, software development and the universe; transmitted by Rob Levine

Unity does not respect HierarchicalLifetimeManager when manually registering UnityServiceLocator with the container

by Rob Levine on 15-Mar-2013

I’ve been trying to phase out some usage of the static ServiceLocator pattern in our codebase. There are many reasons not to like this, which I won’t go into here:

1
var foo = ServiceLocator.Current.GetInstance();

In order to try and make the changes in small incremental steps, my first plan was to allow the IServiceLocator instance to be injected into classes as a dependency at construction time by Unity. This would allow me to make minimal changes; first I’d register an instance of UnityServiceLocator with the DI container, and then make a few small changes to the classes, just switching calls like the above to something like:

1
var foo = serviceLocator.GetInstance();

This gets rid of the rather horrible “static-ness” of the service locator pattern.

Once verified as working, I’d then proceed to inject the actual dependencies themselves, rather than the service locator.

However, I seem to have stumbled across some rather horrible Unity behaviour. What I’m seeing suggests that Unity treats the UnityServiceLocator (the Unity implementation of IServiceLocator) as a static singleton, even when it is configured otherwise, and this behaviour seems to be particular to UnityServiceLocator.

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static void Main(string[] args)
{
    var container = new UnityContainer();
    container.RegisterType(
        new HierarchicalLifetimeManager(),
        new InjectionFactory(
            c =>
                {
                    Console.WriteLine("InjectionFactory invoked with container: " + c.GetHashCode());
                    return new UnityServiceLocator(c);
                }));
 
    container.Resolve(); // expect to see console output here
    container.Resolve(); // don't expect to see console output here
 
    var child = container.CreateChildContainer();
    child.Resolve(); // expect to see console output here
    container.Resolve(); // don't expect to see console output here
 
    var anotherChildContainer = container.CreateChildContainer();
    anotherChildContainer.Resolve(); // expect to see console output here
    anotherChildContainer.Resolve(); // 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, add one constructor which takes IUnityContainer, and have all methods throw NotImplementedException), and swap the line

1
return new UnityServiceLocator(c);

with

1
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 and always makes it a singleton.

This feels like a bug to me, or possibly some undocumented special behaviour. Either way, it is nasty, and cost me a fair bit of time.

ADDENDUM:

Thanks to user Randy Levy (Enterprise Library Support Engineer) on Codeplex for explaining the reason for this behaviour. The UnityServiceLocator itself registers itself with an ExternallyControlledLifetimeManager lifetime:

public UnityServiceLocator(IUnityContainer container)
{
    this.container = container;
    container.RegisterInstance<IServiceLocator>(this, new ExternallyControlledLifetimeManager());
}

The full discussion can be found here.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *