IoC (ServiceLocator and TypeFactory)

Before Catel 2.0, the IoC container used internally was Unity. However, this forced all users to use and configure Unity as the IoC container in their apps and required the shipping of the libraries as well. Since Catel 2.0, a different technique is used which allows the end-developer to use the IoC container technique of their choice.

Also see:

ServiceLocator

Catel uses it's own ServiceLocator implementing the IServiceLocator to gather all services required by Catel. For example, default services are the IPleaseWaitService and the IUIVisualizerService. By default, when the first view model is instantiated, Catel registers all default out of the box services to the ServiceLocator. However, it only does this when the specific services are not already registered. This allows an end-developer to register his/her own implementations of the services before any view model is instantiated (for example, at application startup).

The ServiceLocator can be instantiated, but Catel instantiates one instance that can be used and shared amongst all objects inside the same AppDomain. The ServiceLocator can be retrieved by using ServiceLocator.Instance. The ViewModelBase implements GetService which internally calls ServiceLocator.ResolveType. This way, the end-developer does not have to think about instantiating or retrieving the right ServiceLocator, but can simply use GetService to retrieve the implementation of a specific registered interface.

Registering a type

Registering a type in the ServiceLocator is very simple and works like any other IoC container:

ServiceLocator.Default.RegisterType<IPleaseWaitService, PleaseWaitService>();

Registering an instance of a type

Catel uses Activator.CreateInstance to create the interface implementations when the object is first resolved. However, sometimes a service constructor requires parameters or takes a long time to construct. In such cases, it is recommended to create the type manually and register the instance of the type:

var pleaseWaitService = new PleaseWaitService();
ServiceLocator.Default.RegisterInstance<IPleaseWaitService>(pleaseWaitService);

Registering a type via MissingType event

The ServiceLocator gives the end-developer a last-resort chance to register a type when it is not registered in the ServiceLocator or any of the external containers. This event is very useful for logging (then the developer in the log knows exactly what type is missing from the IoC container) or it can be used to determine at runtime in a very late stage what implementation of the service must be used. To register a type via the event, subscribe to the event and then use the following code:

private void OnMissingType(object sender, MissingTypeEventArgs e)
{
    if (e.InterfaceType == typeof(IPleaseWaitService))
    {
        // Register an instance
        e.ImplementingInstance = new PleaseWaitService();

        // Or a type
        e.ImplementingType = typeof(PleaseWaitService);
    }
}

If both the ImplementingInstance and ImplementingType are filled, the ImplementingIntance will be used.

Resolving a type

To retrieve the implementation of a service, use the following code:

var pleaseWaitService = ServiceLocator.Default.ResolveType<IPleaseWaitService>();

Advanced information

The ServiceLocator is a pretty advanced implementation of IoC without the requirements of any reference to, for example, Unity. There are a few inner workings that are very important that are explained in this section. Internally, this is the workflow that the ServiceLocator use to resolve types:

  1. Check if an instance is already created so it can be returned
  2. Check if the type is registered in the ServiceLocator itself
  3. Loop all external containers and check whether the type is registered there
  4. If the ServiceLocator nor the external containers have the type registered, the ServiceLocator raises the MissingType event which gives the developer a last opportunity to either log the missing type or register it via the event