Поделиться через


Container

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Applications based on the Composite Application Library are composites that potentially consist of many loosely coupled modules. They need to interact with the shell to contribute content and receive notifications based on user actions. Because they are loosely coupled, they need a way to interact and communicate with one another to deliver the required business functionality.

To tie together these various modules, applications based on the Composite Application Library rely on a dependency injection container. The container offers a collection of services. A service provides functionality to other modules in a loosely coupled way through an interface and is often a singleton. The container creates instances of components that have service dependencies. During the component's creation, the container injects any dependencies that the component has requested into it. If those dependencies have not yet been created, the container creates and injects them first.

In some cases, the container itself is resolved as a dependency. For example, modules will often register views of the container by having the container injected.

There are several advantages of using a container:

  • A container removes the need for a component to have to locate its dependencies or manage their lifetime.
  • A container allows swapping the implementation of the dependencies without affecting the component.
  • A container facilitates testability by allowing dependencies to be mocked.
  • A container increases maintainability by allowing new services to be easily added to the system.

Note

For an introduction to dependency injection and inversion of control, see the article Loosen Up - Tame Your Software Dependencies for More Flexible Apps by James Kovacs.

In the context of an application based on the Composite Application Library, there are specific advantages to a container:

  • A container injects module dependencies into the module when it is loaded.
  • A container is used for registering and resolving presenters and views.
  • A container creates presenters and presentation models and injects the view.
  • A container injects the composition services, such as the region manager and the event aggregator.
  • A container is used for registering module-specific services, which are services that have module-specific functionality.

Note

The Stock Trader Reference Implementation (Stock Trader RI) QuickStarts rely on the Unity Application Block as the container. The Composite Application Library itself is not container-specific, and you can use its services and patterns with other containers, such as Castle Windsor, StructureMap, and Spring.NET.

The following code shows how injection works. When the **PositionModule **is created by the container, it is injected with the regionManager and the container. In the RegisterViewsAndServices method, which is called from the module's Initialize method, when the module is loaded, various services, views, and presenters are registered.

public PositionModule(IUnityContainer container, IRegionManager regionManager)
{
    _container = container;
    _regionManagerService = regionManager;
}

public void Initialize()
{
    RegisterViewsAndServices();
    ...
}

protected void RegisterViewsAndServices()
{
    _container.RegisterType<IAccountPositionService, AccountPositionService>(new ContainerControlledLifetimeManager());
    _container.RegisterType<IPositionSummaryView, PositionSummaryView>();
    _container.RegisterType<IPositionSummaryPresentationModel, PositionSummaryPresentationModel>();

    …
}

Using the Container

Containers are used for two primary purposes, namely registering and resolving.

Registering

For services to be available to be injected, they need to be registered with the container. Registering a service involves passing the container a service interface and a concrete type that implements that service. There are primarily two means for registering services: through code or through configuration. The specific means vary from container to container. In the previous code example, you can see how the AccountPositionService is registered with the IAccountPositionService. For an example of entering configuration information through the Unity container, see Entering Configuration Information.

Services have a lifetime. This can be either singleton (a single instance for the container) or instance. The lifetime can be specified at registration or through configuration. The default lifetime depends on the container implementation. For example, the Unity container will register services as instance by default.

Resolving

After a service is registered, it can be resolved or injected as a dependency. When a service is being resolved, and the container needs to create a new instance, it will inject the dependencies into these instances.

In general, when a service is resolved, one of three things will happen:

  • If the service has not been registered, the container will throw an exception.

    Note

    Some containers will allow you to resolve a concrete type that has not been registered.

  • If the service has been registered as a singleton, the container will return the singleton instance. If this is the first time the service was called for, the container will create it and hold on to it for future calls.

  • If the service has not been registered as a singleton, the container will return a new instance and will not hold on to it.

The following code example shows where the IPositionSummaryPresentationModel is being resolved by the container.

IPositionSummaryPresentationModel presentationModel = _container.Resolve<IPositionSummaryPresentationModel>();

The PositionSummaryPresentationModel constructor contains the following dependencies which are injected when it is resolved.

public PositionSummaryPresentationModel(
            IPositionSummaryView view, 
            IAccountPositionService accountPositionService, 
            IMarketFeedService marketFeedSvc, 
            IMarketHistoryService marketHistorySvc, 
            ITrendLinePresenter trendLinePresenter, 
            IOrdersController ordersController, 
            IEventAggregator eventAggregator)

Considerations for Using the Container

You should consider the following before using containers:

  • Consider whether it is appropriate to register and resolve components using the container:
    • Consider whether the performance impact of registering in the container and resolving instances from it is acceptable in your scenario. For example, if you need to create 10,000 polygons to draw a surface within the local scope of a rendering method, the cost of resolving all of those polygon instances through the container might have a significant performance cost because of the container's use of reflection for creating each entity.
    • If there are many or deep dependencies, the cost of creation can increase significantly.
    • If the component does not have any dependencies or is not a dependency for other types, it may not make sense to put it in the container.
  • Consider whether a component's lifetime should be registered as a singleton or instance:
    • If the component is a global service that acts as a resource manager for a single resource, such as a logging service, you may want to register it as a singleton.
    • If the component provides shared state to multiple consumers, you may want to register it as a singleton.
    • If the object that is being injected needs to have a new instance of it injected each time a dependent object needs one, register it as a non-singleton. For example, each Presentation Model needs a new instance of a view.
  • Consider whether you want to configure the container through code or configuration:
    • If you want to centrally manage all the different services, configure the container through configuration.
    • If you want to conditionally register specific services, configure the container through code.
    • If you have module-level services, consider configuring the container through code so that those services are only registered if the module is loaded.
Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.