smartcaveman
smartcaveman

Reputation: 42246

How is the intention of IServiceLocator.GetInstance(Type) different from the intention of IServiceProvider.GetService(Type)?

Is there a difference in intentions of the method signatures IServiceProvider.GetService(Type serviceType) and IServiceLocator.GetInstance(Type serviceType)? If so, what is the distinction?

I've always treated them as equivalent but made a choice to use a single method for consistency. This seems like a good enough solution to dealing with the two interfaces, but I'd really like to know how their usages were actually intended so that I can be sure I am using the right one in the right place. If their intention is in fact the same, then is there any reason for having multiple sets of semantics for the same purpose? (I understand that the GetInstance signature was recommended during the inception of Microsoft.Practices.ServiceLocation, but this doesn't really seem like a sound reason to introduce the duplication).

Why I'm confused

Below is a list of sometimes contradictory facts I have found in trying to find the answer to this question, as well as my interpretation thereof. I am including these so that my question can be addressed in context of all the information that is already known about this topic.

Upvotes: 12

Views: 3181

Answers (2)

Steven
Steven

Reputation: 172646

As you already noted, the difference between IServiceProvider.GetService and IServiceLocator.GetInstance is that any IServiceProvider.GetService implementation should return null when the service isn't registered or when it can't be resolved for what ever reason, while IServiceLocator.GetInstance implementations on the other hand should throw an exception in that case (and never return null).

But note my use of the word 'should'. All the CSL adapters (for Windsor, Spring, Unity and StructureMap, etc) that are shipped with the Common Service Locator project (which owns the IServiceLocator interface) don't adhere to the IServiceProvider interface and they throw an exception when you call their IServiceProvider.GetService method.

By breaking the contract, the designers of the CSL managed to make the IServiceProvider interface utherly useless. You now simply can't rely on it to return null anymore, which is bad. Really bad. The only CSL adapter I know of that adheres to the contract is the Simple Injector adapter, but since all other implementations are broken, even this correctly implemented adapter is useless at that point, since there's no way you can safely swap implementations.

Does this count as a violation of Liskov

Absolutely. They broke the interface contract and implementations can't be substituted from one another.

The designers know about this, as can be seen from Glenn Block's comment on this thread:

Sounds like we might have messed up here. The idea of throwing an exception was an explicit design goal that we all agreed on. Making it implement IServiceProvider was more of a convenience, but sounds like this was overlooked.

The flaw has never been fixed, because the CSL has never been updated.

Upvotes: 12

Guvante
Guvante

Reputation: 19203

I think there is a distinction between the two designs that you didn't mention:

IServiceProvider.GetService
null if there is no service object of type serviceType.

IServiceLocator.GetInstance
ActivationException should be thrown if there is an error resolving the service instance.

It is important to note that one is specifying the default case, while the other is specifying the error case. It makes sense that they would not be the same.

From the samples provided it looks like the combination is the expected implementation. null as default, wrap in ActivationException on error.

EDIT:

Specifically, For any type, T, should an implementation of IServiceLocator.GetAllInstances(typeof(T)) return the same result as IServiceLocator.GetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(T))?

Wouldn't typeof(IEnumerable<T>) work as a more compact form? Additionally, why would GetInstance return anything when asked for an IEnumerable type? You could implement it that way, but I wouldn't call it automatic.

Upvotes: 3

Related Questions