ServiceLocator

Dependency injection container

The ServiceLocation namespace provides abstractions used in PRISM to decouple the actual use of a dependency injection framework (like Unity) from the implementation itself. In other words, by using ServiceLocation and its associated classes, the components that must locate services don't have to depend explicitly on Unity, and it becomes easier to change Unity for another dependency injection framework along the way.

For example, I can use it to get the shell instance in Silverlight:

protected override DependencyObject CreateShell()
{
 return ServiceLocator.Current.GetInstance<Shell>();
}

ServiceLocation classes

From the ServiceLocation namespace there are the classes which this documentation will be focused on:

  • ServiceLocator: a static class to configure and access the current service locator.

  • IServiceLocator: an interface for resolving types.

  • UnityServiceLocatorAdapter: a class to wrap a Unity container.

ServiceLocator use

On the ServiceLocation world there is only one container to resolve dependencies, or using ServiceLocation jargon, there is only one current IServiceLocator to get the desired instances.

ServiceLocator class provides static methods to set and access the current IServiceLocator. This is an example of how to set the current IServiceLocator:

Note on line 1 an instance is being created which implements the IServiceLocator interface. Then it will be explained how to create such an instance later.

Also note on line 2 that the IServiceLocator is not set directly, but using a delegate: the SetLocatorProvider() method receives a function which must return the IServiceLocator which must be used as the current IServiceLocator.

When the current IServiceLocatorCurrent property of ServiceLocator, as follows:

The following section shows how to use the current IServiceLocator to resolve targets.

IServiceLocator use

Once the current IServiceLocator is set on the ServiceLocator, can be used to resolve targets like this:

GetInstance<ITarget>() is a method of IServiceLocator which returns the default mapping registered for type ITarget.

Now that registration of mappings is mencion, it is worth mentioning that IServiceLocator does not handle the registration of mappings: it only handles the resolution of targets. The registration part is left as an implementation detail by the actual dependency container.

And how is an actual dependency container provided? Let's see how in the next section.

UnityServiceLocatorAdapter use

Unity container will be used as the actual dependency container for this example. Creating a Unity container and register mappings on it, but if it does not implement IServiceLocator, it can't be set as the current IServiceLocator:

In order to use the Unity container as the current IServiceLocator it must wrap it in another class called UnityServiceLocatorAdapter, which implements the IServiceLocator interface:

Support Dependency Injection on Typescript

The framework uses TSyringe on TypeScript as the Unity replacement for dependency injection, but TSyringe does not provide an equivalent for ServiceLocation functionality. Therefore, there are implementing a few helper classes which mirror ServiceLocation functionality as close as possible.

ServiceLocator on Typescript

TypeScript does not have an equivalent for static classes, but the functionality of the ServiceLocator can be mimicked using a normal class with a private constructor and static methods, this class is the equivalent of the UnityContainer in Silverlight:

The private constructor prevents creating instances of this class and prevents extending the class, which mimics the effects of a static class in C#.

The setLocationProvider() method works in an equivalent way as SetLocartorProvider() on C#: it receives a function which must return an IServiceLocator. Internally the ServiceLocator class saves this function to use it when the current IServiceLocator is requested.

Finally, current is available as a read only property, which will return the current IServiceProvider. Internally it calls the function set by setLocationProvider() to get the IServicerProvider, or throws an error if such function has not been set.

IServiceLocator on Typescript

IServiceLocator can be implemented as a TypeScript interface:

getInstance<T>() is implemented using TypeScript generics, which allows to specify the type returned. The token is specified without type, which allows us to use types as tokens or strings, in the case of named mappings.

To resolve targets, anyone can do it in the following way:

Interfaces must be resolved in a unique way because interfaces "disappear" when TypeScript gets compiled into JavaScript. For this reason, the string must be used as a token instead of the interface type.

ServiceLocatorAdapter on Typescript

The functionality of UnityServiceLocatorAdapter is implemented in a class named ServiceLocatorAdapter, like this:

Since the use of the DependencyContainer on TypeScript as replacement for Unity containers, ServiceLocatorAdapter receives a DependencyContainer on its constructor. Then it simply redirects the getInstance() call to the resolve() method of the DependencyContainer.

To set the current IServiceLocator on TypeScript:

Note on lines 5 and 6 it is necessary to use the strings as tokens when registering the mapping for an interface, because interfaces "disappear" when TypeScript gets compiled into JavaScript. When registering a class (not interface) the type can be used as token.

Last updated

Was this helpful?