Wednesday, May 12, 2010

To inject IServiceLocator or not to inject

I had a discussion the other day about it being a bad idea to inject the IServiceLocator as a dependency into the constructor of consuming types. Now I'm talking about the Common Service Locator by the p&p team at Microsoft (http://commonservicelocator.codeplex.com/) but to be honest this applies to the Service Locator pattern in general.

The argument was based on the fact that it makes testing more difficult as you not only have to mock or provide types to the constructor but you have to add them to a service locator if injecting the service locator interface.

There are valid reasons for not doing this but we don't live in an ivory tower and sometimes we need to pull types from the container at runtime that we simply do not know at design time. In this case it makes perfect sense for using a service locator, right? as the whole point of the pattern is to abstract container from implementation code.

I'm curious of other developers feel for this and their approach as to whether they do this or not, and if so why.

7 comments:

Daniel Marbach said...

Hy simon,
We do the following in our projects (we are mainly using ninject): Keep the code as container unaware as possible, where dynamic creation of types at runtime is necessary we write a special factory which gets an IResolutionRoot injected. The factory then offers methods as CreateFoo etc. The component which needs to create dynamic types at runtime then gets an IFooFactory injected which the is easily testable.

Daniel

Simon Hart said...

Daniel,

I see, this is interesting. Factory patterns and IoC is ok in my opinion. Some people argue that you should use either factory or IoC but not both as it complicates the architecture but I disagree as somethings arn't as clear cut as that.

Simon

Unknown said...

As Daniel says, sometimes you need to create types dynamically so it is unavoidable that you will need to (directly or indirectly) ask your container to do so.

Personally, I would probably reference the container as a singleton which side-steps the "inject or not" question:
ServiceLocator.Instance.Resolve(key)

The issue around the fact that any code directly querying the container now requires mock services to be injected into the container can be resolved using an auto-mocking container that replaces/overrides your standard container implementation (for testing purposed only!). This automatically returns mocks for any requested service, obviating the need to populate the container manually. e.g. for Castle and Rhino: http://blog.eleutian.com/2007/02/23/TestsAutoMockingIoCContainer.aspx

Chris

Simon Hart said...

@Chris,

Interesting point in accessing the static type ServiceLocator.Instance instead of injecting the IServiceLocator interface.

This approach probably makes it easier to test as you say because you can load the container with mocked classes using your favourite mocking toolset on test initialize.

Although I would think performance would suffer slightly on intialization of tests due to the fact that you have to build the whole container up when running tests instead of just mocking the classes that you need. It's probably a trade off.

Simon

Unknown said...

Yes, that's why I suggested using an auto-mocking container - since they create mocks of requested services on demand you don't necessarily need to explicitly register anything during test initialisation.

Unknown said...

Hi Simon,

Long time no speak.

As lobominator said you want to keep your code as IoC unaware as possible. We use the factory method approach and because we use Windsor we can use Windsor's factory facility which calls the create method on the factory and injects the required type into the constructor instead of needing to inject the factory.

If your constructor is becoming over bloated with injected types it is probably an indication that your ObjectA is trying to do too much. You should consider refactoring some of the code into ObjectB which is a dependency of the ObjectA.

Matt Davis

Simon Hart said...

Matt,

In the ObjectA doing too much scenario. This is a prime example of where the facade pattern could be a good choice.

Simon