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(); |
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(); |
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
} |
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); |
return new UnityServiceLocator(c);
with
1
| return new MyUnityServiceLocator(c); |
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.