Partager via


Setting Up the Unity 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.

The latest Unity Application Block information can be found at the Unity Application Block site.

This scenario explores the process of setting up the Unity container, registering mappings and singletons with the container, registering existing object instances, and specifying configuration information for the container.

Typical Goals

In this scenario, you will see how to set up the Unity container, and any nested containers, so that application code can obtain references to instances of specific concrete objects, based on requests for an interface or a more general object type such as a base class or inherited class.

Solution

There are three ways you can set up and prepare a Unity container by populating it with registered mappings. You can do the following:

  • Provide an XML format configuration file that adheres to the schema shown in Source Schema for the Unity Application Block.
  • Use the methods of the container to add specific registration entries for default and named mappings.
  • Use the container configuration API to provide a custom configuration for the container.

You can use the following two methods in your code to register mappings for a Unity container:

  • RegisterType. This method registers a type with the container. At the appropriate time, the container will build an instance of the type you specify. This could be in response to dependency injection through class attributes or when you call the Resolve method. The lifetime of the object it builds will correspond to the lifetime you specify in the parameters of the method. If you do not specify a value for the lifetime, the type is registered for a transient lifetime, which means that a new instance will be created on each call to Resolve.
  • RegisterInstance. This method registers with the container an existing instance of a type that you specify, with the lifetime that you specify. The container will return the existing instance during that lifetime. If you do not specify a value for the lifetime, the instance will have the default container-controlled lifetime. It will return a reference to the original object on each call to Resolve.

Note

You can use the Unity container to generate instances of any object that has a public constructor (in other words, objects that you can create using the "new" operator), without registering a mapping for that type with the container. When you call the Resolve method and specify the default instance of a type that is not registered, the container simply generates and returns an instance of that type. However, the only time that this is realistically practical is when the object you are generating contains dependency attributes that the container will use to inject dependent objects into the requested object.

The remainder of this topic discusses the ways you can use code to register mappings for a container. It contains the following procedures:

  • To register a mapping for an interface or class to a concrete type
  • To register a class or type as a singleton instance
  • To register an existing object as a singleton instance
  • To use the fluent interface of the container

For details of how to specify configuration using a configuration file, see Entering Configuration Information. For details of how to create custom configurators, see Extending and Modifying the Unity Application Block.

To register a mapping for an interface or class to a concrete type

This procedure demonstrates the most common scenario for dependency injection using Unity. It shows how you can register a mapping between types such as an interface or a base class and a corresponding concrete class that implements or inherits from it.

  1. Create a new instance of the UnityContainer class or use a reference to an existing instance. To create a new instance, you can use the "new" operator.

    IUnityContainer myContainer = new UnityContainer();
    
    'Usage
    Dim myContainer As IUnityContainer = New UnityContainer()
    
  2. If you want to use a child container nested within an existing container, call the CreateChildContainer method of the parent container. For more details about using nested containers, see Using Container Hierarchies.

    IUnityContainer childCtr = myContainer.CreateChildContainer();
    
    'Usage
    Dim childCtr As IUnityContainer = myContainer.CreateChildContainer()
    
  3. Call the RegisterType method of the container in which you want to register a mapping or a type. Specify the registered type as an interface or object type and the target type you want returned in response to a query for that type. The target type must implement the interface, or inherit from the class, that you specify as the registered type. The following code creates a default (un-named) mapping using an interface as the dependency key.

    myContainer.RegisterType<IMyService, CustomerService>();
    
    'Usage
    myContainer.RegisterType(Of IMyService, CustomerService)()
    

    Note

    The API for the Unity container contains both generic and non-generic overloads of most of the methods, so that you can use it with languages that do not support the Generics syntax. For more information, see Unity Application Block Methods.

  4. If you want to map a class or object type to a more specific class that inherits from it, use the same syntax but provide the inherited object as the registered type, as shown in this code.

    myContainer.RegisterType<MyServiceBase, DataService>();
    
    'Usage
    myContainer.RegisterType(Of MyServiceBase, DataService)()
    

    Note

    The registeration type controls how you will retrieve objects from the container. It is the type that you will specify when you call the Resolve or ResolveAll method to retrieve the concrete object instance.

  5. If you want to create more than one registration or mapping for the same type, you can create a named (non-default) mapping by specifying a name as a parameter, as shown in this code.

    myContainer.RegisterType<IMyService, CustomerService>("Customers");
    
    'Usage
    myContainer.RegisterType(Of IMyService, CustomerService)("Customers")
    

Note

If the target class or object specifies any dependencies of its own, using constructor, property, or method call injection attributes, the instance returned will have these dependent object injected automatically. For information about using constructor, property, or method call injection techniques, see Annotating Objects for Constructor Injection, Annotating Objects for Property (Setter) Injection, and Annotating Objects for Method Call Injection.

To register a class or type as a singleton instance

By default, the object instances returned by the container when you use the RegisterType method have a transient lifetime. The container does not store a reference to the object, and you will get a new instance of the type each time you call the Resolve method. If you want to manage the lifetime of the object, you specify a lifetime manager when you call the RegisterType method. The most common scenario is to create an instance that behaves like a singleton, so that the container creates the object the first time you call Resolve and then returns the same instance for all subsequent calls to Resolve for as long as the container is in scope.

  1. Create or obtain a reference to a container as shown in steps 1 and 2 of the preceding procedure, "To register a mapping for an interface or class to a concrete type."

  2. If you want to specify a mapping between an interface or base class and a concrete type, call the RegisterType method of the container in which you want to register a mapping. Specify as the registration type an interface or object type and the target type you want returned in response to a query for that registered type. The target type must implement the interface, or inherit from the class, that you specify as the registered type. Include an instance of the ContainerControlledLifetimeManager class in the parameters to the RegisterType method to instruct the container to register a singleton mapping. The following code creates a default (un-named) singleton mapping using an interface as the registration type.

    myContainer.RegisterType<IMyService, CustomerService>(new ContainerControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterType(Of IMyService, CustomerService)(New ContainerControlledLifetimeManager())
    

    Note

    The API for the Unity container contains both generic and non-generic overloads of most of the methods, so that you can use it with languages that do not support the Generics syntax. For more information, see Unity Application Block Methods.

  3. If you want to map a class or object type to a more specific class that inherits from it, use the same syntax but provide the base class as the registration type, as shown in this code.

    myContainer.RegisterType<MyServiceBase, EmailService>(new ContainerControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterType(Of MyServiceBase, EmailService)(New ContainerControlledLifetimeManager())
    
  4. If you do not want to specify a mapping between an interface or base class and a concrete type, but just register a specific type as a singleton, call the RegisterType method of the container in which you want to register the singleton. Specify as the registration type the concrete type you want returned in response to a query for that type. Include an instance of the ContainerControlledLifetimeManager class in the parameters to the RegisterType method. The following code creates a default (un-named) singleton registration for the type CustomerService.

    myContainer.RegisterType<CustomerService>(new ContainerControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterType(Of CustomerService)(New ContainerControlledLifetimeManager())
    
  5. If you want to create more than one registration using the same registered type, you can create a named (non-default) registration by specifying a name as a parameter, as shown in this code.

    myContainer.RegisterType<CustomerService>("Customers", 
                             new ContainerControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterType(Of CustomerService)("Customers", _
                             New ContainerControlledLifetimeManager())
    
  6. In the same way, if you want to register more than one mapping for an object type that will return a singleton using the registered type, you can create a named (non-default) registration by specifying a name as a parameter, as shown in this code.

    myContainer.RegisterType<IMyService, CustomerService>("Customers", 
                             new ContainerControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterType(Of IMyService, CustomerService)("Customers", _
                             New ContainerControlledLifetimeManager())
    

Note

If the target class or object specifies any dependencies of its own, using constructor, property, or method call injection attributes, the instance returned will have these dependent object injected automatically. For information about using constructor, property, or method call injection techniques, see Annotating Objects for Constructor Injection, Annotating Objects for Property (Setter) Injection, and Annotating Objects for Method Call Injection.

To register an existing object as a singleton instance

In some cases, you may have an existing object that you want to register with the container. By default, the container will take over lifetime management of the object and store a reference to it in the container. The existing object then behaves like a singleton, and the container returns the same instance for all calls to Resolve for as long as the container is in scope.

  1. Create or obtain a reference to a container as shown in steps 1 and 2 of the earlier procedure, "To register a mapping for an interface or class to a concrete type."

  2. Call the RegisterInstance method of the container in which you want 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 code creates a default (un-named) registration for an object of type EmailService that implements the IMyService interface.

    EmailService myEmailService = new EmailService();
    myContainer.RegisterInstance<IMyService>(myEmailService);
    
    'Usage
    Dim myEmailService As New EmailService()
    myContainer.RegisterInstance(Of IMyService)(myEmailService)
    

    Note

    The registration type controls how you will retrieve objects from the container. It is the type that you will specify when you call the Resolve or ResolveAll method to retrieve the concrete object instance.

    The API for the Unity container contains both generic and non-generic overloads of most of the methods, so that you can use it with languages that do not support the Generics syntax. For more information, see Unity Application Block Methods.

  3. If you do not want to map the existing object type to an interface or base class type, you can specify the actual concrete type for the registered type, as shown in this code.

    myContainer.RegisterInstance<EmailService>(myEmailService);
    
    'Usage
    myContainer.RegisterInstance(Of EmailService)(myEmailService)
    
  4. If you want to create more than one existing object registration using the same registered type, you can create named (non-default) mappings by specifying a name as a parameter, as shown in this code.

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

To register an existing object instance as an externally controlled instance

The default lifetime for objects registered using the RegisterInstance method is use of the ContainerControlledLifetimeManager, which causes them to behave like a singleton for the lifetime of the container. However, sometimes you may need to maintain control of the lifetime of existing objects or allow some other mechanism to control the lifetime. You can create your own custom lifetime managers for specific scenarios, but Unity includes the ExternallyControlledLifetimeManager class that provides generic support for externally managed lifetimes. It causes the container to maintain only a weak reference to the object when you register it, allowing other code to maintain the object in memory or dispose it.

  1. Create or obtain a reference to a container as shown in steps 1 and 2 of the earlier procedure, "To register a mapping for an interface or class to a concrete type."

  2. Call the RegisterInstance method of the container in which you want 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. Provide a reference to the existing object and an instance of the ExternallyControlledLifetimeManager class as parameters of the method. The following code creates a default (un-named) externally managed registration using an interface as the registered type.

    EmailService myEmailService = new EmailService();
    myContainer.RegisterInstance<IMyService>(myEmailService, 
                                new ExternallyControlledLifetimeManager());
    
    'Usage
    Dim myEmailService As New EmailService()
    myContainer.RegisterInstance(Of IMyService)(myEmailService, _ 
                                New ExternallyControlledLifetimeManager())
    

    Note

    The API for the Unity container contains both generic and non-generic overloads of most of the methods, so that you can use it with languages that do not support the Generics syntax. For more information, see Unity Application Block Methods.

  3. If you want to register the object using the object type, or a type from which the target object inherits, use the same syntax but provide the actual or the inherited object type as the registration type, as shown in this code.

    myContainer.RegisterInstance<EmailService>(myEmailService, 
                                new ExternallyControlledLifetimeManager());
    // or
    myContainer.RegisterInstance<MyServiceBase>(myEmailService, 
                                new ExternallyControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterInstance(Of EmailService)(myEmailService, _
                                New ExternallyControlledLifetimeManager())
    ' or
    myContainer.RegisterInstance(Of MyServiceBase)(myEmailService, _
                                New ExternallyControlledLifetimeManager())
    

    Note

    The registered type controls how you will retrieve objects from the container. It is the type that you will specify when you call the Resolve or ResolveAll method to retrieve the concrete object instance.

  4. If you want to create more than one existing object registration using the same registered type, you can create named (non-default) mappings by specifying a name as a parameter, as shown in this code.

    myContainer.RegisterInstance<MyServiceBase>("Email",
        myEmailService, new ExternallyControlledLifetimeManager());
    
    'Usage
    myContainer.RegisterInstance(Of MyServiceBase)("Email", _
        myEmailService, New ExternallyControlledLifetimeManager())
    

Note

When you register an existing object, dependency injection based on specified using constructor, property, or method call injection attributes will not take place. You can force property and method call injection to occur by passing the object through the BuildUp method, but you cannot force constructor injection to occur because the class constructor will never execute for an existing object. For information about using constructor, property, or method call injection techniques, see Annotating Objects for Constructor Injection, Annotating Objects for Property (Setter) Injection, and Annotating Objects for Method Call Injection.

To use the fluent interface of the container

The API for the Unity container also provides a fluent interface. This means that you can use a chain of methods in one statement.

  1. To use the fluent interface, call all the methods you want one after the other in a single statement, as shown in this code.

    EmailService myEmailService = new EmailService();
    IUnityContainer myContainer = new UnityContainer()
       .RegisterType<IMyService, DataService>()
       .RegisterType<IMyUtilities, DataConversions>()
       .RegisterInstance<IMyService>(myEmailService);
    
    'Usage
    Dim myEmailService As New EmailService()
    Dim myContainer As IUnityContainer = New UnityContainer() _
       .RegisterType(Of IMyService, DataService)() _
       .RegisterType(Of IMyUtilities, DataConversions)() _
       .RegisterInstance(Of IMyService)(myEmailService) 
    
  2. You can use this approach to chain any of the methods of the UnityContainer class when you create the container. For example, you can use any combination of the RegisterType, RegisterInstance, AddExtension, and Configure methods to prepare the container.

More Information

For more information about the techniques discussed in this scenario, see the following topics: