Jaa


Understanding Lifetime Managers

The Unity container manages the creation and resolution of objects based on a lifetime you specify when you register the type of an existing object, and uses the default lifetime if you do not specify a lifetime manager for your type registration.

When you register a type in configuration, or by using the RegisterType method, the default behavior is for the container to use a transient lifetime manager. It creates a new instance of the registered, mapped, or requested type each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. The container does not store a reference to the object. However, when you want nontransient behavior (such as a singleton) for objects Unity creates, the container must store a reference to these objects. It must also take over management of the lifetime of these objects.

Unity uses specific types that inherit from the LifetimeManager base class (collectively referred to as lifetime managers) to control how it stores references to object instances and how the container disposes of these instances.

When you register an existing object using the RegisterInstance method, the default behavior is for the container to take over management of the lifetime of the object you pass to this method using the ContainerControlledLifetimeManager. This means that at the end of the container lifetime, the existing object is disposed. You can also use this lifetime manager when defining registrations in configuration, or when using the RegisterType method, to specify that Unity should manage the object as a singleton instance.

Using a non-default lifetime manager with RegisterInstance will result in different behaviors, depending on the context of the requests.

  • Resolve requests in the same context where the RegisterInstance call was made, such as the same thread if using a per-thread manager, or the same parent container when using the hierarchical one, will return the registered instances.
  • Resolve requests in other contexts, such as a different thread if using a per-thread manager, or a child container when using the hierarchical lifetime manager, will result in a new instance being created by the container and it will be made the singleton for that context. The creation of an instance under these circumstances could fail if the container cannot resolve the instance, for example if you registered an instance for an interface with no mappings to a matching class.

For information about using lifetime managers with the RegisterType and RegisterInstance methods, see Registering Types and Type Mappings and Creating Instance Registrations in the Run-Time Configuration section of this documentation. For information about specifying the lifetime of objects at design time, see Specifying Types in the Configuration File, and The <instance> Element in the Design-Time Configuration section of this documentation.

Unity Built-In Lifetime Managers

Unity includes six lifetime managers that you can use directly in your code, but you can create your own lifetime managers to implement specific lifetime scenarios. Unity includes the following lifetime managers:

  • TransientLifetimeManager. For this lifetime manager Unity creates and returns a new instance of the requested type for each call to the Resolve or ResolveAll method. This lifetime manager is used by default for all types registered using the RegisterType, method unless you specify a different lifetime manager.

  • ContainerControlledLifetimeManager which registers an existing object as a singleton instance. For this lifetime manager Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects. Unity uses this lifetime manager by default for the RegisterInstance method if you do not specify a different lifetime manager. If you want singleton behavior for an object that Unity will create when you specify a type mapping in configuration or when you use the RegisterType method, you must explicitly specify this lifetime manager.

    If you registered a type mapping using configuration or using the RegisterType method, Unity creates a new instance of the registered type during the first call to the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. Subsequent requests return the same instance.

    If you registered an existing instance of an object using the RegisterInstance method, the container returns the same instance for all calls to Resolve or ResolveAll or when the dependency mechanism injects instances into other classes, provided that one of the following is true:

    • You have specified a container-controlled lifetime manager
    • You have used the default lifetime manager
    • You are resolving in the same context in which you registered the instance when using a different lifetime manager.

    Once you have a reference to the proper container, call the RegisterInstance method of that container to register the existing object. Specify as the registration type an interface that the object implements, an object type from which the target object inherits, or the concrete type of the object.

    The following example creates a named (non-default) mapping by specifying the name, Email and uses the default lifetime.

    myContainer.RegisterInstance<EmailService>("Email", myEmailService);
    
    'Usage
    myContainer.RegisterInstance(Of EmailService)("Email", myEmailService)
    

    When the container is disposed, it calls the Dispose method of the object and allows it to be garbage collected. Therefore, you must ensure that your code does not maintain a reference to the object.

  • HierarchicalLifetimeManager. For this lifetime manager, as for the ContainerControlledLifetimeManager, Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. The distinction is that when there are child containers, each child resolves its own instance of the object and does not share one with the parent. When resolving in the parent, the behavior is like a container controlled lifetime; when resolving the parent and the child you have different instances with each acting as a container-controlled lifetime. If you have multiple children, each will resolve its own instance.

    IUnityContainer Parent = new UnityContainer();
    IUnityContainer child = Parent.CreateChildContainer();
    
    MyType newInstance = new MyType();
    Parent.RegisterInstance<MyType>(newInstance, new HierarchicalLifetimeManager()); 
    
    'Usage
    Dim Parent As IUnityContainer = New UnityContainer()
    Dim child As IUnityContainer = Parent.CreateChildContainer()
    
    Dim newInstance As New MyType()
    Parent.RegisterInstance(Of MyType)(newInstance, New HierarchicalLifetimeManager())
    
  • PerResolveLifetimeManager. For this lifetime manager the behavior is like a TransientLifetimeManager, but also provides a signal to the default build plan, marking the type so that instances are reused across the build-up object graph. In the case of recursion, the singleton behavior applies where the object has been registered with the PerResolveLifetimeManager. The following example uses the PerResolveLifetimeManager.

    public void ViewIsReusedAcrossGraph()
    {
        var container = new UnityContainer()
            .RegisterType<IPresenter, MockPresenter>()
            .RegisterType<IView, View>(new PerResolveLifetimeManager());
        var view = container.Resolve<IView>();
        var realPresenter = (MockPresenter) view.Presenter;
    }
    
    'Usage
    Public Sub ViewIsReusedAcrossGraph()
        Dim container = New UnityContainer() _
            .RegisterType(Of IPresenter, MockPresenter)() _
            .RegisterType(Of IView, View)(New PerResolveLifetimeManager())
        Dim view = container.Resolve(Of IView)()
        Dim realPresenter = DirectCast(view.Presenter, MockPresenter)
    End Sub
    

    The following small object graph illustrates the per-build behavior for the example. MockPresenter inherits from IPresenter and contains a View and MockPresenter object. IView contains a Presenter object and View class contains a Presenter dependency. View is reused across the graph per resolve call because View is registered with a PerResolveLifetimeManager.

    public interface IPresenter 
    { }
    
    public class MockPresenter : IPresenter
    {
        public IView View { get; set; }
    
        public MockPresenter(IView view)
        {
            View = view;
        }
    }
    
    public interface IView
    {
        IPresenter Presenter { get; set; }
    }
    
    public class View : IView
    {
        [Dependency]
        public IPresenter Presenter { get; set; }
    }
    
    'Usage
    Public Interface IPresenter
    End Interface
    
    Public Class MockPresenter
        Implements IPresenter
    
        Private _View As IView
        Public Property View() As IView
            Get
                Return _View
            End Get
            Set(ByVal value As IView)
                _View = value
            End Set
        End Property
    
        Public Sub New(ByVal view__1 As IView)
            View = view__1
        End Sub
    End Class
    
    Public Interface IView
        Property Presenter() As IPresenter
    End Interface
    
    Public Class View
        Implements IView
    
        Private _Presenter As IPresenter
        <Dependency()> _ 
        Public Property Presenter() As IPresenter
            Get
                Return _Presenter
            End Get
            Set(ByVal value As IPresenter)
                _Presenter = value
            End Set
        End Property
    
    End Class
    
  • PerThreadLifetimeManager. For this lifetime manager Unity returns, on a per-thread basis, the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. This lifetime manager effectively implements a singleton behavior for objects on a per-thread basis. PerThreadLifetimeManager returns different objects from the container for each thread.

    If you registered a type mapping using configuration or using the RegisterType method, Unity creates a new instance of the registered type the first time the type is resolved in a specified thread, either to answer a call to the Resolve or ResolveAll method for the registered type or to fulfill a dependency while resolving a different type. Subsequent resolutions on the same thread return the same instance.

    PerThreadLifetimeManager returns the object desired or permits the container to create a new instance if no such object is currently stored for the current thread. A new instance is also created if called on a thread other than the one that set the value.

    Note

    When using the PerThreadLifetimeManager, it is recommended that you use RegisterType and do not use RegisterInstance to register an object.
    When you register an instance with the PerThreadLifetimeManager, the instance is registered only for the executing thread. Calls to Resolve on other threads result in per-thread singletons for container-built instances. If you request a type registered with the PerThreadLifetimeManager in any thread other than the thread it was registered for, the lifetime container for that object finds that there is no registered instance for that thread and, therefore, permits the container to build a new instance for that thread.
    The result is that for threads other than the one registering the instance, the behavior is the same as if you registered the container lifetime with RegisterType.

    This lifetime manager does not dispose the instances it holds. The thread object is reclaimed by garbage collection when the thread is disposed, but note that the object is not disposed in the sense that the Dispose method is not invoked.

  • ExternallyControlledLifetimeManager. The ExternallyControlledLifetimeManager class provides generic support for externally managed lifetimes. This lifetime manager allows you to register type mappings and existing objects with the container so that it maintains only a weak reference to the objects it creates when you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes based on attributes or constructor parameters within that class. This allows other code to maintain the object in memory or dispose it and enables you to maintain control of the lifetime of existing objects or allow some other mechanism to control the lifetime. Using the ExternallyControlledLifetimeManager enables you to create your own custom lifetime managers for specific scenarios. Unity returns the same instance of the registered type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes. However, since the container does not hold onto a strong reference to the object after it creates it, the garbage collector can dispose of the object if no other code is holding a strong reference to it.

Note

Using the RegisterInstance method to register an existing object results in the same behavior as if you just registered the lifetime container with RegisterType. Therefore, it is recommended that you do not use the RegisterInstance method to register an existing object when using the non-default lifetime managers except for the thread in which the RegisterInstance was invoked.

More Information