Friday, October 3, 2008

The Common Service Locator

I have argued against making 'hard' references to an underlying Dependency Injection (or IoC) container; rather, allow the container to do the work for you, namely, inject the dependencies. I realize that it can be difficult in practice, and apparently so have others.

A group of DI container authors have come together to define a common interface for using the underlying containers without creating a hard reference to a particular implementation. This is useful when you are writing code that needs to use a container to resolve a dependency (i.e. get an instance) but you don't know or care what container is being used.

So now we have the Common Service Locator library. Thanks to Ayende for blogging it. UPDATE: Also Glenn Block.

Each container needs an adapter to implement the interface and do the translation from the common method calls to the native container calls. This allows all container references to be declared as Microsoft.Practices.ServiceLocation.IServiceLocator.

Your code has two ways of getting the container:

  • The IServiceLocater reference can be supplied by the calling code and you just use it, or
  • You code can use a static property, ServiceLocator.Current.

Here is the interface that you will use (API Reference):

using System;
using System.Collections.Generic;

namespace Microsoft.Practices.ServiceLocation
{
// Summary:
// The generic Service Locator interface. This interface is used to retrieve
// services (instances identified by type and optional name) from a container.
public interface IServiceLocator : IServiceProvider
{
// Summary:
// Get all instances of the given TService currently registered in the container.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// A sequence of instances of the requested TService.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
IEnumerable<TService> GetAllInstances<TService>();
//
// Summary:
// Get all instances of the given serviceType currently registered in the container.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// Returns:
// A sequence of instances of the requested serviceType.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
IEnumerable<object> GetAllInstances(Type serviceType);
//
// Summary:
// Get an instance of the given TService.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
TService GetInstance<TService>();
//
// Summary:
// Get an instance of the given named TService.
//
// Parameters:
// key:
// Name the object was registered with.
//
// Type parameters:
// TService:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is are errors resolving the service instance.
TService GetInstance<TService>(string key);
//
// Summary:
// Get an instance of the given serviceType.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is an error resolving the service instance.
object GetInstance(Type serviceType);
//
// Summary:
// Get an instance of the given named serviceType.
//
// Parameters:
// serviceType:
// Type of object requested.
//
// key:
// Name the object was registered with.
//
// Returns:
// The requested service instance.
//
// Exceptions:
// Microsoft.Practices.ServiceLocation.ActivationException:
// if there is an error resolving the service instance.
object GetInstance(Type serviceType, string key);
}
}



Here are the containers that have adapters at the moment:





I recommend IServiceLocator be used in all components that might need a reference to a container. Note that they are no common calls for configuring the container because this is a container-specific task that should be handled in the calling code.

No comments: