Export (0) Print
Expand All

Appendix A - Dependency Injection with Unity

Modern business applications consist of custom business objects and components that perform specific or generic tasks within the application, in addition to components that individually address crosscutting concerns such as logging, authentication, authorization, caching, and exception handling. The key to successfully building these types of applications is to achieve a decoupled or very loosely coupled design. Loosely coupled applications are more flexible and easier to maintain. They are also easier to test during development.

What is Dependency Injection?

Dependency injection (DI) is a prime technique for building loosely coupled applications. It provides opportunities to simplify code, abstract and handle dependencies between objects, and automatically generate dependent object instances. Dependency injection describes the process of designing applications so that, rather than specifying concrete dependencies within the application at design time and creating the required objects in the code, the application decides at run time what objects it needs, and generates and injects these into the application.

The use of dependency injection provides several benefits, including:

  • Reducing coupling between classes. Dependencies are clearly defined in each class. The configuration information, and mappings between interfaces or base classes and the actual concrete types, are stored in the container used by the dependency injection mechanism, and can be updated as required—without requiring any changes to the run-time code.
  • Making your code more discoverable. You can easily tell from the types of the constructors, properties, or methods of your classes what objects they use and what dependencies they have. If you create instances using code inside the classes, it is more difficult to trace dependencies. Resolving dependencies at the surface of a class by specifying the types or interfaces it requires and taking advantage of dependency injection is the recommended approach.
  • Making testing easier. If you resolve or obtain objects using code within your classes, you must provide a suitably configured container for use when unit testing these classes. If you take advantage of dependency injection, you can create simple mock test objects for your classes to use.

The Unity Dependency Injection and Interception Mechanism

Unity is a lightweight, flexible, configurable, and extensible dependency injection container that supports constructor, property setter, and method call injection (as well as instance and type interception). It is provided as an integral part of Enterprise Library and is also available as a stand-alone DI mechanism. Unity provides a comprehensive set of capabilities, and makes it easier to implement common dependency inversion patterns and techniques that are useful in application architecture, design, and development.

Unity provides a container that you use to store type mappings and registrations. You can create multiple containers, and nest these containers in a hierarchical fashion, if required. It also supports extensions that allow you to implement extra functionality for objects resolved through the container. Unity can generate instances of any object that has a public constructor (in other words, objects that you can create using the new operator).

As Unity creates each object, it inspects it for dependencies and automatically populates these. So, for example, if you specify a parameter for the constructor or a dependent property of a custom class to be of type MyBusinessComponent, Unity will create an instance of MyBusinessComponent and populate the constructor or property. However, if MyBusinessComponent defines a dependency on another class named MyDataComponent, Unity will create an instance of that class and populate that dependency, and so on. If there is no mapping in the container for the type specified in the parameter or property, Unity simply creates a new instance of the specified type by calling the constructor of that type that has the greatest number of parameters, and returns it.

Imagine that MyDataComponent requires a LogWriter to create log messages. If it contains a dependent parameter or property of type LogWriter, Unity will populate that as part of the instantiation process as well. This process (sometimes referred to as auto-wiring) can apply right across the defined dependencies in your application, as shown in Figure 1.

Figure 1

A possible object graph for a business component

Ff953186.c501942e-fbe0-4524-a3b5-9a695c1e89b5-thumb(en-us,PandP.50).png


Summary of Unity Features

Unity is more than just a simple dependency injection container. It provides advanced capabilities that allow you to satisfy a wide range of requirements that can help you build more decoupled applications. Unity allows you to:

  • Register mappings between interfaces or base classes and concrete object types. When you resolve the interface or base type, Unity will return an instance of the concrete type. This may be a new or an existing instance, depending on the lifetime you specify for the registration.
  • Register instances of existing objects in the container. When you resolve the type, Unity will return an instance of that type. This is useful when working with objects that must be instantiated as single instances (singletons), or must have a non-standard lifetime, such as services used by your application. Unity will return the existing instance.
  • Specify the lifetime of objects that will be resolved through the container. Unity can resolve objects based on the singleton pattern; or with a weak reference when the object is managed by another process. It can also be configured to return instances on a per-thread basis, where a new instance is created for each thread while an existing instance is returned for calls on the same thread.
  • Define multiple named registrations for a type. You can create more than one registration or mapping for a type as long as these registrations and mappings have a unique name. Registrations and mappings that do not have a name are known as default registrations and default mappings, where the name is effectively an empty string.
  • Construct an entire object graph for an application that will be resolved at run time. Unity can automatically resolve the types specified in constructor and method parameters and properties at run time. The parameters of constructors or methods of objects that are resolved through the container, or the values for properties of objects resolved through the container, can be populated with an object resolved through another registration within the container, or an instance of the specified type if no matching registration exists.
  • Specify values for constructor and method parameters and properties. These can be the parameters of constructors or methods of objects that are resolved through the container, or the values for properties of objects resolved through the container. The value can be specified directly, or it can be an object resolved through another registration within the container.
  • Define matching rules and behaviors as part of an interception policy. These can be used to apply business rules or change the behavior of existing components. Calls to methods or properties of these objects will then pass through a policy pipeline containing one or more interception behaviors. This is a similar approach to that used in aspect-oriented programming (AOP).
  • Add custom extensions to the container. These can extend or change the behavior of the container when resolving objects. Some Unity features, such as interception, are powered by a container extension that is included with the standard Unity installation.

Defining Dependencies with Unity

Unity provides three ways for you to define the dependencies for each of your custom types:

  • You can define all of your dependency injection and interception requirements using a configuration file. At run time, you use a single line of code to read the configuration and load it into a Unity container.
  • You can use the methods of the container that register types, type mappings, parameter and property values, and interception requirements in your code. You can also use these methods to modify any existing registrations in the container at run time.
  • You can apply attributes to define dependencies for constructor and method parameters and properties of types that you will resolve through the container. This is a simple approach, but does not provide the same level of control as using a configuration file or the run-time container API.

You can also use a mixture of all of these techniques; for example, you can register a mapping in the container between an interface and a concrete implementation, then use an attribute to define a dependency for a property or parameter on an implementation of this interface.

Table 1 will help you to choose the best approach for your own requirements.

Table 1

Defining dependencies

Technique

Description

Considerations

Configuration-based

Define dependencies using registrations and mappings loaded into the container from a file or other configuration source.

Makes it easy to change the registrations and dependency mappings; often by just editing the configuration file. However, users of the classes do not see the dependencies in the source code. This approach is flexible and allows comprehensive configuration of injection of resolved type instances, fixed values, and arrays.

Dynamic registration

Define dependencies using registrations and mappings within the container by generating them dynamically at run time using code.

Changes to the registrations and dependency mappings require you edit the code, though this is usually only in one location in a startup file. However, users of the classes do not see the dependencies in the source code. This approach is flexible and allows comprehensive configuration of injection of resolved type instances, fixed values, and arrays.

Attribute-based

Use attributes applied to parameters and properties within the classes to define the dependencies.

Makes the dependencies obvious and easy to see in the source code of classes, but requires you to edit the source code when you need to change the dependencies. This approach is also less flexible and less comprehensive than the other approaches.


In addition, you can change the behavior of the dependency resolution mechanism in several ways:

  • You can specify parameter overrides or dependency overrides that set the values of specific parameters.
  • You can define optional dependencies, so that Unity will set the value of a parameter or property to null if it cannot resolve the type of the dependency.
  • You can use deferred resolution, so that the resolution does not take place until the target type is actually required or used in your code.
  • You can specify a lifetime manager that will control the lifetime of the resolved type.

The following sections of this appendix describe some of the more common techniques for defining dependencies in your classes though constructor, property, and method call injection. We do not discuss interception in this appendix. For full details of all the capabilities and uses of Unity, see the Unity section of the documentation installed with Enterprise Library and available online at http://go.microsoft.com/fwlink/?LinkId=188875.

Constructor Injection

By default, Unity will attempt to resolve and populate the types of every parameter of a class constructor when you resolve that type through the container. You do not need to configure or add attributes to a class for this to occur. Unity will choose the most complex constructor (usually the one with the largest number of parameters), resolve the type of each parameter through the container, and then create a new instance of the target type using the resolved values.

The following are some simple examples that demonstrate how you can define constructor injection for a type.

Automatic Constructor Injection

If you have a class that contains a non-default constructor, Unity will automatically populate any dependencies defined in the parameters of the constructor. For example, the following type has a dependency on a type named Database.

public class MyNewObject
{
  public MyNewObject(Database defaultDB)
  { 
    // code to use the resolved Database instance here
  }
} 

If you need to change the behavior of the automatic constructor injection process, perhaps to specify the lifetime of the resolved type or to set the value or lifetime of the types resolved for the parameters, you can configure the container at design time using a configuration file or at run time using the container API.

Design-Time Configuration

Configuring constructor injection in a configuration file is useful when you need to exert control over the process. For example, consider the following class that contains a single constructor that takes two parameters.

public class MyNewObject
{
  public MyNewObject(Database defaultDB, string departmentName)
  { 
    ...
  }
} 

The second parameter is a string, and Unity cannot generate an instance of a string type unless you have registered it in the container using a named instance registration. Therefore, you must override the default behavior of the automatic injection process. You can do this in a configuration file, and at the same time manage three aspects of the injection process: the resolved object lifetime, the value of parameters, and the choice of constructor when the type contains more than one constructor.

For example, you can use the following register directive in a configuration file to specify that the resolved instance of MyNewObject should be a singleton (with its lifetime managed by the container), that Unity should resolve the type Database of the parameter named defaultDB and inject the result, and that Unity should inject the string value "Customer Service" into the parameter named departmentName.

<register type="MyNewObject">
  <lifetime type="singleton" />
  <constructor>
    <param name="defaultDB" />
    <param name="departmentName" value="Customer Service" />
  </constructor>
</register>

When you specify constructor injection like this, you are also specifying which constructor Unity should use. Even if the MyNewObject class contains a more complex constructor, Unity will use the one that matches the list of parameters you specify in the register element.

To register your types using named registrations, you simply add the name attribute to the register element, as shown here.

<register type="MyNewObject" name="Special Customer Object">
  ...
</register>

To register mappings between an interface or base class and a type that implements the interface or inherits the base type, you add the mapTo attribute to the register element. You can, of course, define default (unnamed) and named mappings in the same way as you do type registrations. The following example shows registration of a named mapping.

<register type="IMyType" mapTo="MyImplementingType" 
          name="Special Customer Object">
  ...
</register>

Run-Time Configuration

You can configure injection for the default or a specific constructor at run time by calling the RegisterType method of the Unity container. This approach also gives you a great deal of control over the process. The following code registers the MyNewObject type with a singleton (container-controlled) lifetime.

myContainer.RegisterType<MyNewObject>(new ContainerControlledLifetimeManager());

If you want to create a named registration, you add the name as the first parameter of the RegisterType method, as shown here.

myContainer.RegisterType<MyNewObject>("Special Customer Object", 
                         new ContainerControlledLifetimeManager());

If you want to create a mapping, you specify the mapped type as the second generic type parameter, as shown here.

myContainer.RegisterType<IMyType, MyImplementingType>(
                         "Special Customer Object", 
                         new ContainerControlledLifetimeManager());

If you need to specify the value of the constructor parameters, such as a String type (which Unity cannot create unless you register a String instance with the container), or specify which constructor Unity should choose, you include an instance of the InjectionConstructor type in your call to the RegisterType method. For example, the following creates a registration named Special Customer Object for the MyNewObject type as a singleton, specifies that Unity should resolve the type Database of the parameter named defaultDB and inject the result, and that Unity should inject the string value "Customer Service" into the parameter named departmentName.

myContainer.RegisterType<MyNewObject>(
            "Special Customer Object", 
            new ContainerControlledLifetimeManager(),
            new InjectionConstructor(typeof(Database), "Customer Service")
);

Configuration with Attributes

When you specify just the type in a constructor parameter, as shown earlier, the container will return the default concrete implementation of that type as defined in the registrations within the container. To specify a named registration when using constructor injection, you can add the Dependency attribute to the parameter definition, as shown below.

public class MyNewObject
{
  public MyNewObject([Dependency("CustomerDB")] Database customers)
  { 
    // code to use the resolved Database instance here
  }
} 

If your class has multiple constructors, and you want to specify the one Unity will use, you apply the InjectionConstructor attribute to that constructor, as shown in the code excerpt that follows. If you do not specify the constructor to use, Unity chooses the most complex (usually the one with the most parameters). This technique is useful if the most complex constructor has parameters that Unity cannot resolve.

public class MyNewObject
{
  public MyNewObject(Database defaultDB, string departmentName)
  { 
    ...
  }

  [InjectionConstructor]
  public MyNewObject(Database defaultDB)
  { 
    ...
  }
} 

Property (Setter) Injection

Property (setter) injection can populate one or more properties of your custom classes at run time. Unlike constructor injection, property injection does not occur by default. You must specify the dependency using a configuration file, programmatically at run time, or by applying an attribute to the property that holds the dependent type.

Design-Time Configuration

To define property injection using a configuration file, you simply specify the names of the properties that Unity should populate within the register element. If you want Unity to resolve the type specified by the property, you need do no more than that. If you want to specify a value, you can include this within the property element. If you want Unity to use a named registration within the container to resolve the type, you include the dependencyName attribute in the property element. Finally, if you want to resolve a type that is compatible with the property name, such as resolving an interface type for which you have named mappings already registered in the container, you specify the type to resolve using a dependencyType attribute.

The following excerpt from a configuration file specifies dependency injection for three public properties of a type named MyOtherObject. Unity will resolve whatever type the BusinessComponent property of the MyOtherObject type is defined as through the container and inject the result into that property. It will also inject the string value "CorpData42" into the property named DataSource, and resolve the type ILogger using a mapping named StdLogger and inject the result into the Logger property.

<register type="MyOtherObject">
  <property name="BusinessComponent" />
  <property name="DataSource" value="CorpData42" />
  <property name="Logger" dependencyName="StdLogger" dependencyType="ILogger" />
</register>

Run-Time Configuration

You can configure injection for any public property of the target class at run time by calling the RegisterType method of the Unity container. This gives you a great deal of control over the process. The following code performs the same dependency injection process as the configuration file example you have just seen. Notice the use of the ResolvedParameter type to specify the named mapping that Unity should use to resolve the ILogger interface.

myContainer.RegisterType<MyOtherObject>(
            new InjectionProperty("BusinessComponent"),
            new InjectionProperty("DataSource", "CorpData42"),
            new InjectionProperty("Logger", 
                new ResolvedParameter(typeof(ILogger), "StdLogger")
            )
);

You can use the ResolvedParameter type in constructor and method call injection as well as in property injection, and there are other types of injection parameter classes available for even more specialized tasks when configuring injection.

Configuration with Attributes

To specify injection for a property, you can alternatively apply the Dependency attribute to it to indicate that the type defined and exposed by the property is a dependency of the class. The following code demonstrates property injection for a class named MyNewObject that exposes as a property a reference to an instance of the type Database.

public class MyNewObject
{
  [Dependency]
  public Database CustomerDB { get; set; }
} 

When you apply the Dependency attribute without specifying a name, the container will return the type specified as the default (an unnamed registration) or a new instance of that type. To specify a named registration when using property injection with attributes, you include the name as a parameter of the Dependency attribute, as shown below.

public class MyNewObject
{
  [Dependency("LocalDB")]
  public Database NamedDB { get; set; }
} 

Method Call Injection

Method call injection is a less common approach than constructor and property setter injection, but is useful in two specific situations. Firstly, constructor injection only works when you are instantiating new instances of objects (when the constructor is executed), whereas method call injection will work with existing instances of objects. For example, Unity will execute the method when it resolves an instance that is registered as a singleton, or when you call the BuildUp method of the container.

Secondly, while property setter injection also works with existing instances, it requires public properties to be exposed. Using method call injection means that you do not need to expose public properties to be able to inject values into existing instances of resolved types.

The usual approach is to expose a public initialization method that takes as parameters the objects you want to resolve and obtain references to. Unity will populate the parameters and then call the method. As the method executes, you store the resolved types in local variables of your class.

Method call injection does not occur by default, and must be configured using a configuration file, programmatically at run time, or by applying an attribute to the method.

Design-Time Configuration

The techniques for specifying dependency injection for method parameters is very similar to what you saw earlier for constructor parameters. The following excerpt from a configuration file defines the dependencies for the two parameters of a method named Initialize for a type named MyNewObject. Unity will resolve the type of the parameter named customerDB through the container and inject the result into that parameter of the target type. It will also inject the string value "Customer Services" into the parameter named departmentName.

<register type="MyNewObject">
  <method name="Initialize">
    <param name="customerDB" />
    <param name="departmentName" value="Customer Services" />
  </method>
</register>

You can also use the dependencyName and dependencyType attributes to specify how Unity should resolve the type for a parameter in exactly the same way as you saw for property injection. If you have more than one overload of a method in your class, Unity uses the set of parameters you define in your configuration to determine the actual method to populate and execute.

Run-Time Configuration

As with constructor and property injection, you can configure injection for any public method of the target class at run time by calling the RegisterType method of the Unity container. The following code achieves the same result as the configuration extract you have just seen.

myContainer.RegisterType<MyNewObject>(
     new InjectionMethod("Initialize", typeof(Database), "CustomerServices") 
);

In addition, you can specify the lifetime of the type, and use named dependencies, in exactly the same way as you saw for constructor injection.

Configuration with Attributes

You can apply the InjectionMethod attribute to a method to indicate that any types defined in parameters of the method are dependencies of the class. The following code demonstrates the most common scenario, saving the dependent object instance in a class-level variable, for a class named MyNewObject that exposes a method named Initialize that takes as parameters instances of the type Database and an instance of a concrete type that implements the ILogger interface.

public class MyNewObject
{
  private Database theDB;
  private ILogger theLogger;

  [InjectionMethod]
  public void Initialize(Database customerDB, ILogger loggingComponent) 
  {
    // assign the dependent objects to class-level variables
    theDB = customerDB;
    theLogger = loggingComponent;
  }
} 

You can also add the Dependency attribute to a parameter to specify the name of the registration Unity should use to resolve the parameter type, just as you saw earlier for constructor injection with attributes. And, as with constructor injection, all of the parameters of the method must be resolvable through the container. If any are value types that Unity cannot create, you must ensure that you have a suitable registration in the container for that type, or use a dependency override to set the value.

More Dependency Injection Opportunities

In addition to the techniques we have shown here for defining dependencies, Unity allows you to specify both the type to resolve, and its dependencies, as generic types. You can also specify dependencies that are arrays of any type, including generic types. You can even have Unity resolve all the members of an array automatically, or specify individual members of the array yourself.

Resolving Populated Instances of Your Classes

After you have defined your object graph dependencies, you must resolve the type at the root of this hierarchy through the container to initiate the dependency injection process. In Unity, you use the Resolve method to kick off the process by specifying the type of the object whose dependencies you want Unity to populate. The following code resolves a populated instance of the MyNewObject type from the container.

MyNewObject theInstance = container.Resolve<MyNewObject>();

This returns the type registered as the default (no name was specified when it was registered). If you want to resolve a type that was registered with a name, you specify this name as a parameter of the Resolve method. You might also consider using implicit typing instead of specifying the type, to make your code less dependent on the results of the resolve process.

var theInstance = container.Resolve<MyNewObject>("Registration Name");

Alternatively, you may choose to define the returned type as the interface type when you are resolving a mapped type. For example, if you registered a type mapping between the interface IMyType and the concrete type MyNewObject, you should consider using the following code when you resolve it.

IMyType theInstance = container.Resolve<IMyType>();

Writing code that specifies an interface instead of a particular concrete type means that you can change the configuration to specify a different concrete type without needing to change your code. Unity will always return a concrete type (unless it cannot resolve an interface or abstract type that you specify; in which case an exception is thrown).

You can also resolve a collection of types that are registered using named mappings (not default unnamed mappings) by calling the ResolveAll method. This may be useful if you want to check what types are registered in your run-time code, or display a list of available types. However, Unity also exposes methods that allow you to iterate over the container and obtain information about all of the registrations.

We don't have room to provide a full guide to using Unity here. However, this discussion should have given you a taste of what you can achieve using dependency injection. For more detailed information about using Unity, see the documentation installed with Enterprise Library and available online at http://go.microsoft.com/fwlink/?LinkId=188874.


Show:
© 2015 Microsoft