Using Container Hierarchies
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.
|The latest Unity Application Block information can be found at the Unity Application Block site.|
The Unity Application Block supports nested containers, allowing you to build container hierarchies. Nesting containers provides two useful capabilities for managing dependency injection and object lifetime. They allow you to do the following:
- Control the scope and lifetime of singleton object instances
- Registering Different Mappings for Specific Types
When the container creates singleton objects, it manages the lifetime of these singletons. They remain in scope until you (or the garbage collector) dispose the container. At this point, it disposes the registered singleton instances it contains. In addition, if you dispose the parent container in a nested container hierarchy, it automatically disposes all child containers and the registered singletons they contain.
Therefore, if you require two separate sets of such objects that must have different lifetimes, you can use hierarchical containers to store and manage each set. Register instances that you want to be able to dispose separately in one or more child containers that you can dispose without disposing the parent container.
The following code demonstrates the use of a child container to manage the lifetime of specific singleton instances while maintaining the singleton instances in the parent container.
// Create parent container IUnityContainer parentCtr = new UnityContainer(); // Register type in parent container parentCtr.RegisterType<MyParentObject>(new ContainerControlledLifetimeManager()); // Create nested child container in parent container IUnityContainer childCtr = parentCtr.CreateChildContainer(); // Register type in child container childCtr.RegisterType<MyChildObject>(new ContainerControlledLifetimeManager()); // Create instance of type stored in parent container MyParentObject parentObj = parentCtr.Resolve<MyParentObject>(); // Create instance of type stored in child container MyChildObject childObj = childCtr.Resolve<MyChildObject>(); // ... can use both generated objects here ... // Dispose child container childCtr.Dispose(); // ... can use only object in parent container here ... // Dispose parent container parentCtr.Dispose();
|You can also specify the lifetime of objects by using the LifetimeManager parameter when you register types or existing objects with the container. For more information about how to use the LifetimeManager parameter, see System Design When Using a Dependency Injection Container and Registering Existing Object Instances. For more information about how to create custom lifetime managers, see Creating Lifetime Managers.|
You can use nested containers when you have slightly different dependency injection requirements for specific objects but want to provide a "fallback" facility for objects that implement a specific interface or are of a specific type. For example, you may have a general requirement for objects that implement the IMyObject interface to map to the type MyStandardObject. However, in specific parts of the application code, you may want the IMyObject interface to map to the type MySpecialObject.
In this case, you can register the general mapping in the parent container and register the specific case in a child container. Then, when you want to obtain an instance of the object, you call the Resolve method on the appropriate container. If you call the method on the child container, it returns an object of type MySpecialObject. If you call the method on the parent container, it returns an object of type MyStandardObject.
However, the advantage with nested containers is that, if the child container cannot locate a mapping for the requested interface or type, it passes the request to its parent container and onward through the hierarchy until it reaches the "root" or "base" container. Therefore, for objects not mapped in the child container, the mapping in the parent container (or in an ancestor container where there are more than two levels in the hierarchy) defines the object type returned.
The following code shows how you can implement the preceding scenario.
// Create parent container IUnityContainer parentCtr = new UnityContainer(); // Register two mappings for types in parent container parentCtr.RegisterType<IMyObject, MyStandardObject>(); parentCtr.RegisterType<IMyOtherObject, MyOtherObject>(); // Create nested child container in parent container IUnityContainer childCtr = parentCtr.CreateChildContainer(); // Register mapping for specific type in child container childCtr.RegisterType<IMyObject, MySpecialObject>(); // Now retrieve instances of the mapped objects using the child container. // Using the interface as the type for the returned objects means that it // does not matter which container returns the actual object. // This code returns an object of type MySpecialObject using the mapping // registered in the child container: IMyObject specialObject = childCtr.Resolve<IMyObject>(); // This code returns an object of type MyOtherObject using the mapping // registered in the parent container because there is no mapping in // the child container for this type: IMyOtherObject otherObject = childCtr.Resolve<IMyOtherObject>(); // Now retrieve instance of the standard object using the parent container. // This code returns an object of type MyStandardObject using the mapping // registered in the parent container: IMyObject standardObject = parentCtr.Resolve<IMyObject>(); // Dispose parent container and child container parentCtr.Dispose();