Appendix B - Dependency Injection in Enterprise Library

This appendix discusses some of the more advanced topics that will help you to obtain the maximum benefit from Enterprise Library in terms of creating objects and managing the dependency injection container. It includes the following:

  • Loading configuration information into a Unity container
  • Viewing the registrations in the container
  • Populating entire object graphs at application startup
  • Maintaining a reference to the container in request-based applications
  • Using an alternative service locator or dependency injection container

These topics provide information about how you can use the more sophisticated dependency injection approach for creating instances of Enterprise Library objects, as described in Chapter 1, "Introduction." If you have decided not to use this approach, and you are using the Enterprise Library service locator and its GetInstance method to instantiate Enterprise Library types, they are not applicable to your scenario.

Loading Configuration Information into a Unity Container

Unlike many applications, and unlike the application blocks within Enterprise Library, Unity does not automatically load configuration information when it starts. This is intentional; it means that you can load configuration information into one or more new or existing containers, including containers that you create as a hierarchy of parent and child containers.

This also means that you can exert considerable control over how requests for types are handled. For example, you can use multiple containers to specify dependencies for different parts of your application, while allowing requests that cannot be satisfied in a child container to pass up through the hierarchy of parent containers until a suitable registration is found.

It also means that you can load configuration information from different sources. A typical example is loading configuration from a file other than App.config or Web.config, or by adding registrations programmatically by—for example—reading them from a database and applying them to the container.

The Unity container class exposes the LoadConfiguration method that you can use to populate a container. You can call this method with no parameters to read a <unity> section from the current application configuration file (App.config or Web.config), as demonstrated in Chapter 1 of this guide. Alternatively, you can provide the method with a UnityConfigurationSection instance that contains the configuration information. The following code opens a configuration file using the methods of the Microsoft® .NET Framework configuration system, casts it to a UnityConfigurationSection type, and loads the registrations in the <container> section that has the name MyContainerName into a new Unity container.

// Read a specified config file using the .NET configuration system.
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = @"c:\configfiles\myunityconfig.config";
System.Configuration.Configuration config 
                     = ConfigurationManager.OpenMappedExeConfiguration(map,
                                            ConfigurationUserLevel.None);
// Get the unity configuration section.
UnityConfigurationSection section
    = (UnityConfigurationSection)config.GetSection("unity");

// Create and populate a new UnityContainer with the configuration information.
IUnityContainer theContainer = new UnityContainer();
theContainer.LoadConfiguration(section, "MyContainerName");

You can define multiple containers within the <unity> section of a configuration file providing each has a unique name, and load each one into a separate container at run time. If you do not assign a name to a container in the configuration file, it becomes the default container, and you can load it by omitting the name parameter in the LoadConfiguration method.

To load a container programmatically in this way, you must add the System.Configuration.dll assembly and the Microsoft.Practices.Unity.Configuration.dll assembly to your project. You should also import the following namespaces:

  • Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity
  • Microsoft.Practices.Unity

Viewing Registrations in the Container

Sometimes you may find that your application throws an error indicating that it cannot resolve a specific type. The error messages that Unity returns are detailed, and should help you to find the problem quickly. However, you may find it useful to be able to browse the contents of the container to see the registrations and mappings it contains.

The Unity container exposes the Registrations property, which returns a collection of ContainerRegistration instances; one for each registration or type mapping in the container. The following example code shows how you can extract details for each registration: the registered type, the type it maps to (if any), the name of the registration (if it is not a default registration), and the lifetime manager type.

foreach (ContainerRegistration item in theContainer.Registrations)
{
  regType = item.RegisteredType.Name;
  mapTo = item.MappedToType.Name;
  regName = item.Name ?? "[default]";
  lifetime = item.LifetimeManagerType.Name;
  if (mapTo != regType)
  {
    mapTo = " -> " + mapTo;
  }
  else
  {
    mapTo = string.Empty;
  }
  lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length);
  // Display details of the registration as appropriate.
}

Populating Entire Object Graphs at Application Startup

After you populate the container with your configuration information, both the Enterprise Library information and the registrations and mappings for your own custom types, you can resolve these custom types with all of their dependencies populated through dependency injection. You can define dependencies in three ways:

  • As one or more parameters of a constructor in the target class. Unity will create instances of the appropriate types and populate the constructor parameters when the target object is instantiated. This is the approach you will typically use. For example, you can have Unity automatically create and pass into your constructor an instance of a LogWriter or an ExceptionManager, store the reference in a class variable or field, and use it within that class.
  • As one or more properties of the target class. Unity will create an instance of the type defined by the property or in configuration and set that instance as the value of the property when the class is resolved through the container.
  • As one or more parameters of a method in the target class. Unity will create instances of the appropriate types and populate the method parameters when the target object is instantiated, and then call that method. You can store the references passed in the parameters in a class variable or field for use within that class. This approach is typically used when you have an Initialize or similar method that should execute when the class is instantiated.

By taking advantage of this capability to populate an entire object graph, you may decide to have the container create and inject instances of the appropriate types for all of the dependencies defined in your entire application when it starts up (or, at least, a significant proportion of it).

While this may seem to be a strange concept, it means that you do not need to hold onto a reference to the container after you perform this initial population of dependencies. That doesn’t mean you cannot hold onto the container reference as well, but resolving all of the required types at startup can improve run-time performance at the cost of slightly increased startup time. Of course, this also requires additional memory and resources to hold all of the resolved instances, and you must balance this against the expected improvement in run-time performance.

You can populate all of your dependencies by resolving the main form or startup class through the container. The container will automatically create the appropriate instances of the objects required by each class and inject them into the parameters and properties. However, it does rely on the container being able to create and return instances of types that are not registered in the container. The Unity container can do this. If you use an alternative container, you may need to preregister all of the types in your application, including the main form or startup class.

Typically, this approach to populating an entire application object graph is best suited to applications built using form-based or window-based technologies such as Windows® Presentation Foundation (WPF), Windows Forms, console applications, and Microsoft Silverlight® (using the version of Unity specifically designed for use in Silverlight applications).

For information about how you can resolve the main form, window, or startup class of your application, together with example code, see the documentation installed with Enterprise Library or available online at http://go.microsoft.com/fwlink/?LinkId=188874.

Maintaining a Container Reference in Request-Based Applications

When using the default Unity DI mechanism with Enterprise Library, all you need to do is initialize the container once on your application, and then use it to resolve (or obtain) instances of Enterprise Library objects or your own classes and objects. Initializing the container requires just the following single line of code.

// Create and populate the default container with application configuration.
var container = new UnityContainer()
                    .AddNewExtension<EnterpriseLibraryCoreExtension>();

However, to use the container to resolve types throughout your application, you must hold a reference to it. You can store the container in a global variable in a Windows Forms or WPF application, in the Application dictionary of an ASP.NET application, or in a custom extension to the InstanceContext of a Windows Communication Foundation (WCF) service.

Table 1 will help you to understand when and where you should hold a reference to the container in forms-based and rich client applications built using technologies such as Windows Forms, WPF, and Silverlight.

Table 1

Holding a reference to the container in forms-based and rich client applications

Task

When

Where

Create and configure container.

At application startup.

Main routine, startup events, application definition file, or as appropriate for the technology.

Obtain objects from the container.

At application startup, and later if required.

Where appropriate in the code.

Store a reference to the container.

At application startup.

Global application state.

Dispose the container.

When the application shuts down.

Where appropriate in the code or automatically when the application ends.


Table 2 will help you to understand when and where you should hold a reference to the container in request-based applications built using technologies such as ASP.NET Web applications and Web services.

Table 2

Holding a reference to the container in request-based applications

Task

When

Where

Create and configure container.

At application startup.

HTTP Module (ASP.NET and ASMX), InstanceContext extension (WCF).

Obtain objects from the container.

During each HTTP request.

In the request start event or load event. Objects are disposed when the request ends.

Store a reference to the container.

At application startup.

Global application state or service context.

Dispose the container.

When the application shuts down.

Where appropriate in the code.


For more detailed information about how you can maintain a reference to the container in different types of applications, in particular, request-based applications, and the code you can use to achieve this, see the documentation installed with Enterprise Library or available online at http://go.microsoft.com/fwlink/?LinkId=188874.

Using an Alternative Service Locator or Container

Enterprise Library, by default, uses the Unity dependency injection mechanism to create instances of Enterprise Library objects. If you are already using, or plan to use, a different dependency injection container in your application you may be able to use it to create Enterprise Library objects instead of using Unity.

For this to work, you can obtain or write your own configurator that can load the container with the Enterprise Library configuration information you specify, or create a type that implements the IServiceLocator interface and can expose the configuration information.

The default behavior of Enterprise Library is to create a new Unity container, create a new configurator for the container, and then read the configuration information from the application's default configuration file (App.config or Web.config). The following code extract shows the process that occurs.

var container = new UnityContainer();
var configurator = new UnityContainerConfigurator(container);

// Read the configuration files and set up the container.
EnterpriseLibraryContainer.ConfigureContainer(configurator, 
                           ConfigurationSourceFactory.Create());
// The container is now ready to resolve Enterprise Library objects

The task of the configurator is to translate the configuration file information into a series of registrations within the container. Enterprise Library contains only the UnityContainerConfigurator, though you can write your own to suit your chosen container, or obtain one from a third party.

An alternative approach is to create a custom implementation of the IServiceLocator interface that may not use a configurator, but can read the application configuration and return the appropriate fully populated Enterprise Library objects on demand.

See http://commonservicelocator.codeplex.com for more information about the IServiceLocator interface.

To keep up with discussions regarding alternate configuration options for Enterprise Library, see the forums on CodePlex at http://www.codeplex.com/entlib/Thread/List.aspx.



Show: