left-icon

Microsoft Unity Succinctly®
by Ricardo Peres

Previous
Chapter

of
A
A
A

CHAPTER 3

Dependency Injection

Dependency Injection


Introduction

A topic close to IoC is Dependency Injection (DI). We call DI to the ability of an IoC container to automatically set properties and method parameters in newly created component instances, as necessary and recursively. Unity fully supports DI in its core, integrated with IoC. When you ask for an instance, Unity injects all its dependencies automatically but you can also ask it to inject dependencies explicitly. Let’s see how we do that.

Injection Types

Unity supports the following five kinds of dependency injection:

  • Automatic injection: When Unity is asked to create a component that takes parameters in its constructor, it automatically obtains values for them when invoking the constructor.
  • Constructor injection: Unity will pass a registered component as a parameter to a constructor, therefore allowing the usage of constructors with parameters.
  • Property injection: Unity sets values for properties from registered components.
  • Method injection: Unity calls initialization methods automatically.
  • Injection factory: Unity calls a custom delegate that builds things for us.

All of these injections, except factory injection, are performed automatically when Unity resolves and creates instances of components.

Auto Injection

If Unity is asked to instantiate a class that has a constructor with parameters, and Unity has unnamed registered components of the same types, then Unity automatically retrieves these components and passes them to the constructor of the class to build. For instance:

public class SomeClass

{

    public SomeClass() { }

    public SomeClass(ILogger logger) { }

}

Even if Unity finds a parameterless constructor and one that takes parameters, it will always choose the one with parameters—as long as it is public and Unity can find registered components that match its types. In this case, it will pass an ILogger instance, which is obtained exactly as if we would be calling Resolve, meaning, this instance can be a singleton or a newly created one depending on the lifetime manager associated with it.

Note: In case you are wondering, Lazy<T> and Func<T> also work here.

Configuring Injection by Attributes

Constructor Injection

We can explicitly control some aspects of the injection process. First, we can select the injection constructor by applying an InjectionConstructorAttribute attribute to an existing public constructor:

public class SomeClass

{

    public SomeClass() { }

    [InjectionConstructor]

    public SomeClass(ILogger logger, IUnityContainer unity) { }

    public SomeClass(ILogger logger) { }

}

Tip: Do not add InjectionConstructorAttribute attributes to more than one constructor because it will confuse Unity and it won’t know what constructor to use.

If we have a component registered as multiple types with different names, we can select which to inject by applying a DependencyAttribute attribute with a name to the constructor argument:

public class SomeClass

{

    public SomeClass() { }

    public SomeClass([Dependency("Console")] ILogger logger) { }

}

Property Injection

If we want a property to be injected as well, it needs to have a setter and it needs to have applied a DependencyAttribute attribute, with or without a name:

public class SomeClass

{

    [Dependency("File")]

    public ILogger File { getset; }

    [Dependency("Console")]

    public ILogger Console { getset; }

}

You can override a registration by applying a PropertyOverride declaration. See Resolving with Overrides.

Method Injection

Finally, method injection: We tell Unity to execute a method after the instance is built. For that purpose, we apply the InjectionMethodAttribute attribute to a public method as in the following example:

public class SomeClass

{

    [InjectionMethod]

    public void Initialize() { }

}

Of course, you can also apply InjectionMethodAttribute to a method with parameters if you want them to be injected by Unity:

public class SomeClass

{

    [InjectionMethod]

    public void Initialize(ILogger logger) { }

}

And if you want to use a registration with a particular name as a method parameter:

public class SomeClass

{

    [InjectionMethod]

    public void Initialize([Dependency("Console")] ILogger logger) { }

}

Tip: The InjectionConstructorAttribute, DependencyAttribute and InjectionMethodAttribute attributes can only be applied to public members.

Tip: The DependencyAttribute and InjectionMethodAttribute attributes can only be applied to the registered type (concrete class), not the key (interface or abstract base class); otherwise they have no effect.

Configuring Injection by Code

Other than by using attributes, it is possible to specify dependencies entirely by code. This is good so that you can apply injections to types that you don’t control or when you don’t want to add references to injection attributes that have no meaning to the classes being injected.

Code injection is configured at registration type; the RegisterType method takes additional parameters in the form of an optional array of InjectionMember instances. The included subclasses of InjectionMember (itself an abstract class) are:

Constructor Injection

Here are some examples. First, here’s how to supply base type parameters for a constructor:

public class SomeClass

{

    public SomeClass(string key, int num) { }

}

unity.RegisterType<SomeClassSomeClass>("Arguments"

    new InjectionConstructor("Some Name", 10));

Next, here’s a constructor with resolved parameters (types registered in Unity):

public class SomeClass

{

    public SomeClass(ILogger logger) { }

}

unity.RegisterType<SomeClassSomeClass>(

    new InjectionConstructor(new ResolvedParameter<ILogger>("Console"));

And you can even mix both plain values and resolved types, like in this example where we are injecting a string and a resolved ILogger named “Console”:

public class SomeClass

{

    public SomeClass(string key, ILogger logger) { }

}

unity.RegisterType<SomeClassSomeClass>(

    new InjectionConstructor("Some Name", new ResolvedParameter<ILogger>("Console"));

So, you pass parameters to InjectionConstructor, which must either be static values or instances of an InjectionParameterValue-derived class (most often ResolvedParameter or ResolvedParameter<T>). The number of values and their order and types dictate which constructor shall be called. ResolvedParameter takes two parameters, one for the type and the other for the optional name, which can be null for default registrations. ResolvedParameter<T> only requires the name because the type is inferred from the generic parameter.

Property Injection

Of course, you can also configure which properties shall have their values injected. In the next example, we inject an ILogger named “Console” to the Logger property:

public class SomeClass

{

    public ILogger Logger { getset; }

}

unity.RegisterType<SomeClassSomeClass>(new InjectionProperty("Logger", new ResolvedParameter<ILogger>("Console"));

And we can also inject plain values as well:

public class FileLogger : ILogger

{

    public string Name { getset; }

}

unity.RegisterType<ILoggerFileLogger>(new InjectionProperty("Name", "output.log"));

Method Injection

Code configuration also allows us to configure method injection and its eventual parameters, as in this example:

public class SomeClass

{

    public void Initialize(ILogger logger) { }

}

unity.RegisterType<SomeClassSomeClass>(

new InjectionMethod("Initialize", new ResolvedParameter<ILogger>("Console")));

Or with no parameters at all:

public class SomeClass

{

    public void Initialize() { }

}

unity.RegisterType<SomeClassSomeClass>(new InjectionMethod("Initialize));

Tip: All constraints from mapping by attributes apply: All members must be public and injected properties must have getters and setters.

Factory Injection

Finally, we can also specify exactly how our component will be built. This will be the most complete solution to creating instances since you can write your own code for that purpose. We use a special injection member called InjectionFactory:

public class SomeClass

{

    public SomeClass(string key) { }

    public ILogger Logger { getset; }

    public void Initialize() { }

}

unity.RegisterType<SomeClassSomeClass>(

    new InjectionFactory((u) => { var x = new SomeClass("Some Name"){ Logger =   new FileLogger("output.log") }; x.Initialize(); return (x); }));

InjectionFactory takes a delegate that receives a single parameter of type IUnityContainer and returns any kind of object. Of course, you should return an instance of the registered component type or one that inherits from it.

Tip: When using InjectionFactory, all of the other injection techniques—constructor, property, and method—do not apply; it’s all up to you.

Putting It All Together

One final example on how we can mix constructor, property, and method injection:

public class SomeClass

{

    public SomeClass(string key) { }

    public ILogger Logger { getset; }

    public void Initialize() { }

}

unity.RegisterType<SomeClassSomeClass>(

    new InjectionConstructor("Some Name"),

    new InjectionMethod("Initialize"),

    new InjectionProperty("Logger", new ResolvedParameter<ILogger>("Console")));

Configuring Injection by XML

Constructor Injection

Constructor, property, and method injections can be configured in XML. An example for setting a simple string value in a constructor is achieved by means of the constructor element:

<register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.FileLogger, 

Succinctly" name="FileLogger">

    <lifetime type="singleton" />

    <constructor>

        <param name="filename" value="output.log"/>

    </constructor>

</register>

One param element must exist for each of the constructor’s parameters. The param takes the following attributes:

  • name (required): The name of the constructor parameter.
  • value (optional): A base type that can be converted from a string representation. If not present, then dependencyType must be supplied.
  • dependencyType (optional): A type name to resolve from Unity. It must be specified unless value is.
  • dependencyName (optional): A registration name to be taken together with dependencyType for resolving a component from Unity.

Property Injection

If we want to set a property from a Unity registration of type ILogger and name “File”, we use a property:

<register type="Succinctly.SomeClass, Succinctly" mapTo="Succinctly.SomeClass, 

Succinctly">

    <lifetime type="transient" />

    <property name="Logger" dependencyName="File" 

        dependencyType="Succinctly.ILogger, Succinctly" />

</register>

The property element takes the same parameters as param of constructor so there’s no point in discussing it here.

Method Injection

Finally, calling a method as part of the initialization process—the method element:

<register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.FileLogger, 

Succinctly" name="FileLogger">

    <lifetime type="singleton" />

    <method name="Initialize" >

        <param name="filename" value="output.log"/>

    </method>

</register>

Again, its parameters are exactly the same as those of the constructor element.

Note: There is not an out-of-the-box way to use InjectionFactory in XML but the chapter entitled “Chapter 5  Extending Unity” presents a simple workaround.

Tip: Don’t forget to call LoadConfiguration() otherwise the XML configuration will be ignored by Unity.

Explicit Injection

If we have an instance of a component that wasn’t obtained through Unity, or if its dependency configuration has somehow changed, we can ask Unity to inject all dependencies explicitly. For that purpose, we have the BuildUp method from IUnityContainer or the BuildUp<T> generic extension method from UnityContainerExtensions.

BuildUp takes an instance and a type. Here are two examples, one with automatic type inference and the other with explicit type specification:

unity.BuildUp(instance); //same as unity.BuildUp(instance.GetType(), instance)

unity.BuildUp(typeof(IMyService), svc);

The reason why you can specify a type is because different base types can have different dependency configurations defined upon them.

You can call BuildUp multiple times and the result should always be the same as long as the injection rules don’t change; all injection methods are called and all injection properties are set.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.