Share via


Entering Configuration Information

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 Enterprise Library information can be found at the Enterprise Library site.

The Unity Application Block can read configuration information from an XML configuration file. By default, this is the App.config or Web.config file for your application. However, you can load configuration information from any other XML format file or from other sources. The following sections of this topic describe the configuration of the Unity Application Block in more detail:

Format of the Unity Configuration File

The following XML shows the outline schema of the configuration file. You must edit this file using a text editor (such as the Visual Studio text editing pane) if you want to provide configuration information for the application block. For details of each element and attribute that you can use in the configuration file for the Unity Application Block configuration, see Source Schema for the Unity Application Block and Unity Configuration Schematic.

<?xml version="1.0" encoding="utf-8" ?> 
<configuration>

<configSections>
  <section name="unity"
            type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
                 Microsoft.Practices.Unity.Configuration, Version=1.1.0.0,
                 Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>

<unity>

  <typeAliases>

    <!-- Lifetime manager types -->
    <typeAlias alias="singleton"
         type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,
               Microsoft.Practices.Unity" />
    <typeAlias alias="external"
         type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager,
               Microsoft.Practices.Unity" />

    <!-- User-defined type aliases -->
    <typeAlias alias="IMyInterface"
         type="MyApplication.MyTypes.MyInterface, MyApplication.MyTypes" />
    <typeAlias alias="MyRealObject" 
         type="MyApplication.MyTypes.MyRealObject, MyApplication.MyTypes" />
    <typeAlias alias="IMyService"
         type="MyApplication.MyTypes.MyService, MyApplication.MyTypes" />
    <typeAlias alias="MyDataService"
         type="MyApplication.MyTypes.MyDataService, MyApplication.MyTypes" />
    <typeAlias alias="MyCustomLifetime" 
         type="MyApplication.MyLifetimeManager, MyApplication.MyTypes" />

  </typeAliases>

  <containers>

    <container name="containerOne">

      <types>

        <!-- Type mapping with no lifetime — defaults to "transient" -->  
        <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass" />

        <!-- Type mapping using aliases defined above -->  
        <type type="IMyInterface" mapTo="MyRealObject" name="MyMapping" />

        <!-- Lifetime managers specified using the type aliases -->
        <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass">
          <lifetime type="singleton" /> 
        </type>
        <type type="IMyInterface" mapTo="MyRealObject" name="RealObject">
          <lifetime type="external" />
        </type>

        <!-- Lifetime manager specified using the full type name -->
        <!-- Any initialization data specified for the lifetime manager -->
        <!-- will be converted using the default type converter -->
        <type type="Custom.MyBaseClass" mapTo="Custom.MyConcreteClass">
          <lifetime value="sessionKey"
                    type="MyApplication.MyTypes.MyLifetimeManager,
                          MyApplication.MyTypes" />
        </type>

        <!-- Lifetime manager initialization using a custom TypeConverter -->
        <type type="IMyInterface" mapTo="MyRealObject" name="CustomSession">
          <lifetime type="MyCustomLifetime" value="ReverseKey"
                    typeConverter="MyApplication.MyTypes.MyTypeConverter,
                                   MyApplication.MyTypes" />
        </type>

        <!-- Object with injection parameters defined in configuration -->
        <!-- Type mapping using aliases defined above -->  
        <type type="IMyService" mapTo="MyDataService" name="DataService">
          <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement,
                                     Microsoft.Practices.Unity.Configuration">
            <constructor>
              <param name="connectionString" parameterType="string">
                <value value="AdventureWorks"/>
              </param>
              <param name="logger" parameterType="ILogger">
                <dependency />
              </param>
            </constructor> 
            <property name="Logger" propertyType="ILogger" />
            <method name="Initialize">
              <param name="connectionString" parameterType="string">
                <value value="contoso"/>
              </param>
              <param name="dataService" parameterType="IMyService">
                <dependency />
              </param>
            </method>
          </typeConfig>
        </type>

      </types>

      <instances>
        <add name="MyInstance1" type="System.String" value="Some value" />
        <add name="MyInstance2" type="System.DateTime" value="2008-02-05T17:50:00"  />
      </instances>

      <extensions>
        <add type="MyApp.MyExtensions.SpecialOne" />
      </extensions>

      <extensionConfig>
        <add name="MyExtensionConfigHandler"
             type="MyApp.MyExtensions.SpecialOne.ConfigHandler" />
      </extensionConfig>

    </container>

    <!-- ... more containers here ... -->

  </containers>

</unity>

</configuration>

Loading Configuration Information into a Container

The Unity Application Block does not automatically read the configuration information or create and prepare containers. To provide maximum flexibility, you use code in your application to instantiate a Unity container and then populate it with the registrations, type mappings, and extensions defined in the configuration file. This allows you to create a nested container hierarchy from the configuration information and to manage the lifetime of each container.

If the configuration defines only a single unnamed container or specifies the default container, the following code will instantiate a new Unity container and then load it with the registrations, type mappings, and extensions defined in the configuration file for that container.

IUnityContainer container = new UnityContainer();
UnityConfigurationSection section
  = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
'Usage
Dim container As IUnityContainer = New UnityContainer()
Dim section As UnityConfigurationSection _
  = CType(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
section.Containers.Default.Configure(container)

To load the configuration information for a specific named container, you use the container name defined in the configuration instead of referencing the default container. For example, if you have a container defined in the configuration with the name containerOne, you can instantiate and load it using the following code.

IUnityContainer container = new UnityContainer();
UnityConfigurationSection section 
  = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers["containerOne"].Configure(container);
'Usage
Dim container As IUnityContainer = New UnityContainer()
Dim section As UnityConfigurationSection _
  = CType(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
section.Containers("containerOne").Configure(container)

This means that you can dispose containers when required by managing the variables that hold references to your containers.

To create a hierarchy of nested containers from configuration information, you simply create the containers in the required hierarchy using the CreateChildContainer method and then load each one with the appropriate container information. The following code shows an example of instantiating and loading two containers from a configuration file that contains registrations, type mappings, and extensions for two containers named containerOne and nestedChildContainer.

IUnityContainer parentContainer = new UnityContainer();
IUnityContainer childContainer = parentContainer.CreateChildContainer();
UnityConfigurationSection section 
  = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers["containerOne"].GetConfigCommand().Configure(parentContainer);
section.Containers["nestedChildContainer"].Configure(childContainer); 
'Usage
Dim parentContainer As IUnityContainer = New UnityContainer()
Dim childContainer As IUnityContainer =  parentContainer.CreateChildContainer()
Dim section As UnityConfigurationSection _
  = CType(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
section.Containers("containerOne").GetConfigCommand().Configure(parentContainer)
section.Containers("nestedChildContainer").Configure(childContainer) 

Note

You cannot nest containers in the configuration file. All <container> elements reside at the same "level" within the <containers> element. You configure nested containers by creating the containers in the required hierarchy in your code and then populating them from the appropriate <container> elements. If required, you can load more than one container from the same <container> element.

Using Alternative Configuration Sources

You can also use any XML configuration file or other source of configuration information if required. For example, you can load configuration information into containers from a specific named configuration file by using the .NET System.Configuration.Configuration class to retrieve the information from any XML formatted file. The following code shows how you can read configuration information from a file named MyConfig.config.

ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = "MyConfig.config";
System.Configuration.Configuration config 
  = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); 
UnityConfigurationSection section
  = (UnityConfigurationSection)config.GetSection("unity");
IUnityContainer container = new UnityContainer();
section.Containers["myContainer"].Configure(container);
'Usage
Dim map As New ExeConfigurationFileMap()
map.ExeConfigFilename = "MyConfig.config"
Dim config As System.Configuration.Configuration _
  = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None) 
Dim section As UnityConfigurationSection _
  = CType(ConfigurationManager.GetSection("unity"), UnityConfigurationSection)
Dim container As IUnityContainer = New UnityContainer()
section.Containers("myContainer").Configure(container)

Dynamically Configuring Constructor, Property, and Method Injection

You can configure the Unity container to perform injection at run time by creating and populating instances of the configuration classes directly. This provides an alternative approach to using attributes within the target classes, or configuring injection requirements using the Unity configuration file.

The classes InjectionConstructor, InjectionProperty, and InjectionMethod have constructors that you can use, in conjunction with the Configure and ConfigureInjectionFor methods, to specify dependency injection parameters for the container.

The following example shows how you can configure constructor, property, and method injection using the configuration classes through the fluent interface of the container.

IUnityContainer myContainer = new UnityContainer();
myContainer.Configure<InjectedMembers>()
  .ConfigureInjectionFor<MyObject>(
    new InjectionConstructor(12, "Hello Unity!"),
    new InjectionProperty("MyProperty"),
    new InjectionProperty("MyStringProperty", "SomeText"),
    new InjectionMethod("InitializeMe", 42.0, 
    new ResolvedParameter(typeof(ILogger), "SpecialLogger"))
  );
'Usage
Dim myContainer As IUnityContainer = New UnityContainer()
myContainer.Configure(Of InjectedMembers)() _
  .ConfigureInjectionFor(Of Foo)( _
    New InjectionConstructor(12, "Hello Unity!"), _
    New InjectionProperty("MyProperty"), _
    New InjectionProperty("MyStringProperty", "SomeText"), _
    New InjectionMethod("InitializeMe", 42.0, _
    New ResolvedParameter(GetType(ILogger), "SpecialLogger")) _
  )

The code shown above creates a container and then configures the following injection requirements:

  • It configures constructor injection for the constructor that accepts as parameters an Integer and a String value, and passes the values 12 and "Hello Unity!" to these parameters.
  • It configures property injection for the property named MyProperty to use the default configuration of the container. The container will resolve the value for this property using registrations and mappings within the container.
  • It configures property injection for the property named MyStringProperty to use the specified value. The container will set the property to the value "SomeText".
  • It configures method injection for the method named InitializeMe that accepts as parameters a Double value and an instance of a class that implements the ILogger interface. It will pass to the first parameter the value 42.0. It will also resolve the ILogger type through the container by looking for a mapping for that type with the name SpecialLogger, and pass the result to the second parameter of the method.

The style of the API for dynamic injection configuration is based on that used in Linq to XML. The intention is to provide sufficient flexibility, while still making it easy and intuitive to use.

You can pass arbitrary objects to the various injection class constructors. However, there are a few rules that you must follow. These are the following:

  • If the object is an instance of the InjectionParameterValue class, the injection system will use the object.
  • If the object is of type Type, the injection system will create a ResolvedParameter that the container will use to resolve an instance of that type.
  • In all other cases, injection system will create an InjectionParameter instance and the container will apply the specified value.

Note

The injection rules are located in the ToParameter method of the InjectionParameterValue class. You can examine this class to see the implementation. The classes used to perform injection (InjectionConstructor, InjectionProperty, and InjectionMethod) are subclasses of the InjectionMember base class. You can create your own implementations based on this class if you want to extend or change the behavior to suit your own specific requirements. You can also create new implementations to replace the InjectionParameterValue class if you want to customize the handling of individual parameters.