Now the Service Container (Inversion of Control and Dependency Injection) concepts are being adopted by the Microsoft Pattern & Practices group in their CAB and EntLib frameworks, maybe I can talk about a service container implementation I made a few months ago (BTW: A service is a course-grained piece of reusable functionality and not -necessarily- a web service!).
I noticed that everyone who has made a service container (or service locator) framework implemented a custom container interface. But the System.ComponentModel contains a decent IServiceProvider interface that is a suitable client interface. It defines one method GetService that takes a Type parameter to specify the service (interface) type. Visual Studio uses a service container when (form/web) controls are hosted on a designer surface.
So I set out to design the basic structure that is needed for building a service container. After examinig the System.ComponentModel namespace further an IComponent, IContainer and an ISite interface came to light. It appears that these interfaces are used in the standard service container mechanism that is already present in the .NET framework.
The (I)Container manages a collection of (I)Component instances (the Container is NOT a ServiceContainer). When a Component is added to a (and only one) Container it is ‘sited’. A (I)Site is what ties a Component to its Container. Notice that the ISite interface is derived from IServiceProvider interface. So, whenever the Component needs a service it only needs to ask its Site using the GetService method for the service it requires.
The default .NET implementation creates a no-functionality Site instance for each Component that is added to a Container. Luckily for us there’s a virtual method on the Container class (CreateSite) we can override to create a Site of our own making. We need to because we still have no ServiceContainer so far and the default implementation provider by the System.ComponentModel doesn’t provide in one, either.
The way I see it is that the Site provides a way to give each Component a unique context and because the Site already implements the IServiceProvider interface it is the logical place to place the ServiceContainer. My design explicitly defines a ServiceContainer class but logically the Site and ServiceContainer could be thought of as one and the same. This means that it is possible to regulate the service implementations each component has access to through its Site.
This means, for example, that if you have a WebPart page and you bootstrap each WebPart (=Component) to a Container, you can control what services are provided to each WebPart. Or if you have a Domain Object Model (=Component) and bootstrap each instance to a Container, you could control the way these instances perform their tracing, because you control the actual Trace Service implementation used for these objects. Must be said that I assume that only a Service interface is published to the clients (Components) not its implementation class.
But how does the framwork know what services to load for a specific component? The framework looks in the .config file for that. The .config file contains a section that describes services. What classes to use and maybe some service specific configuration settings. It also contains a service profile section. A service profile is a list of services that is loaded into a ServiceContainer. At this point there’s also room to specify custom service config settings. Finaly there’s a context binding section. This section maps (for instance) a Component’s name to a service profile that describes the services available for that Component. Future implementations of the framework will probably also include a service policy section to describe settings like lifetime management and other runtime aspects. Lifetime management of the Service instances is not implemented yet. At the moment the Service instance is created (on demand) and cached in the ServiceContainer. Singleton or PerCall (or custom) instance management is something you’ll want to have eventually.
What will happen if a Component requests a service that is not available in its Site/ServiceContainer. The framework allows for a Component hierarchy, where a Component may host a Container that contains their child Component instances. So, if a service request can not be satisfied, it is passed to the parent ServiceContainer and so on, untill the root ServiceContainer is reached. This also implies that service clients (Components) can handle the absence of a service they request (handling this scenario can involve throwing an exception ofcourse ;-).
The root ServiceContainer also contains the Services used by the framework itself. The way the framework obtains its configuration settings is implemented as a service, which gives you the option to replace the configuration source.
Take a look at the gotdotnet workspace (if you’re still interested 😉 where you can download a short ppt and access the source code.Future plans for this framework include incorporating the ObjectBuilder (providing a free DI framework) and providing an alternate Configuration Service for the EntLib configuration app block.
Any thoughts, suggestions or comments are most welcome.