Domanda

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.

  • The MSDN documentation for IServiceProvider says that the GetService(Type serviceType) method should return

    A service object of type serviceType.
    -or-
    null if there is no service object of type serviceType.
  • The MSDN documentation for IServiceLocator lacks method documentation but the summary in the VS Object Browser of GetInstance(Type serviceType) says that the method returns "the requested service instance". However, there is also an exception entry in the documentation IServiceLocator that says that an ActivationException should be thrown if there is an error resolving the service instance.

  • ActivationException is located in the Microsoft.Practices.ServiceLocation namespace which was introduced years after IServiceProvider was introduced. So, it is understandable that the IServiceProvider does not refer to the exception. That being said, the IServiceLocator interface's documentation says nothing about returning null if no result is found. It also isn't clear whether or not the absence of an implementation of the requested service type should constitute an exception.

  • Should the absence of an implementation for a service type cause an ActivationException in IServiceLocator implementations? It doesn't look like it. The implementation template for IServiceLocator ignores any concept of a non-null post-condition.

  • The implementation template for IServiceLocator also treats IServiceProvider.GetService(Type) as alternative syntax for IServiceLocator.GetInstance(). Does this count as a violation of Liskov (due to throwing an exception in subtype that is not declared on the base type), or, would that actually require a difference in the implementation rather than the exceptions declared on the interface's method signatures? What I'm getting at is: Are we sure that the ServiceLocatorImplBase implementation template for IServiceLocator implements both interfaces correctly? Would it be a better representation of the interfaces' intentions for the IServiceProvider to wrap the GetInstance call in a try block, and return null when an exception is caught?

  • Addendum: One other issue related to this is the correspondence of IServiceLocator.GetAllInstances(Type) to IServiceLocator.GetInstance(Type). 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))? (It's easy to see how this relates to the IServiceProvider correspondence, but I think it's better to keep the question simple and only compare the two methods of the same interface for this case.)

È stato utile?

Soluzione

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.

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top