Synchronization with external containers

Registering external containers

As explained earlier, the ServiceLocator supports external containers to make sure that the end-developer can use the IoC container of his/her choice and is not forced by Catel to use any specific IoC implementation. Registering a container is very simple and can be done at any time (but, must of course be done before service registered in the external container can be used by the ServiceLocator itself):

ServiceLocator.Default.RegisterExternalContainer(myUnityContainer);

Registering an external container makes it possible to resolve types from the ServiceLocator that are originally registered in the external container. For example, a database repository is registered in Unity, but must be used inside a view model. This is what happens:

  1. End-developer registers ICustomerRepository in a Unity container
  2. End-developer registers the external container in the ServiceLocator of Catel
  3. In a view model, the developer uses GetService<ICustomerRepository>() to retrieve the repository
  4. Internally, the ServiceLocator checks which external container has the type and uses that specific container to return the type

Synchronization between containers

Until now, it all seems very nice. However, there are some aspects that are very hard and which must be taken into account. For example, think of the following issues:

  •  Instance caching
  • Which container is the actual owner of a type?

Synchronization from external containers to ServiceLocator

 Internally, the ServiceLocator keeps a reference of all registered types in the internal container, all instantiated types and the original container per type. For example, think of the following situation:

  1. A type is registered in Unity (but the ServiceLocator does not know whether it's a type or instance since Unity makes no difference between them)
  2. A developer resolves the type in the container

The ServiceLocator checks whether there is already an instance created or requested earlier by the developer. If it already has an instance in the cache, it simply returns the instance from the cache. If not, it checks which container (it might be itself, but also one of the external containers) is the owner of the type. A container is considered the owner if the type was first found on that specific container. The ServiceLocator lets the external container (in this case Unity) instantiate the type. Then the ServiceLocator stores the instance in the instance cache so it can be easily resolved in the next call.

Synchronization from ServiceLocator to external containers

Great, now think of this situation:

  1.  A type is registered in the ServiceLocator
  2. The type must be injected into a constructor using Unity

This is a bit harder because the type is not registered in Unity, but it is used by Unity. Luckily, the ServiceLocator is able to automatically register types in all external containers as soon as a type is registered or resolved. This way, when a type is registered in the ServiceLocator, it automatically calls ExternalContainer.RegisterType (or (ExternalContainer.RegisterInstance in case of an instance) for all external containers. This way, it is possible to register a type in the ServiceLocator, but still be able to use the powerful dependency injection of Unity.

Automatic or manual synchronization

By default, the ServiceLocator automatically keeps all the IoC containers in sync so the end-developer should not have any knowledge of the inner workings of the ServiceLocator. Sometimes, a developer wants to have more control and needs to disable this automatic behavior. This is possible using the following property:

ServiceLocator.Instance.AutomaticallyKeepContainersSynchronized = false;

Then it is still possible to manually synchronize the containers using the following methods:

  • ExportInstancesToExternalContainers => exports instances only
  • ExportToExternalContainers => exports instances and registered types

External containers that only support instances

There are external containers, such as MEF, that only support the registration of instances, not types. If such an external container is registered, all the types registered in the ServiceLocator are instantiated and registered as an instance in the external container. If this behavior is not acceptable, disable the automatic synchronization and handle the synchronization manually via the AutomaticallyKeepContainersSynchronized property.

External containers supported

Currently, the following external IoC containers are supported:

  • Castle Windsor
  • MEF
  • Ninject
  • Unity

If there is need for other containers, just let us know!