In my previous post on dependency injection I discussed a simple way to enable dependency injection using configuration and System.Activator.  The method described would be sufficient for many cases but may not offer enough power for every situation.  This time we will explore the use of an Inversion of Control container, namely Microsoft’s Unity Application Block.

Taking the "another tool in the tool box" approach let’s explore unity using our previous example of a system which requires multiple authentication methods.

I downloaded and installed Microsoft Enterprise Library 4.1 (October 2008) which you can download here.  If you do not need the entire Enterprise Library (EL) then you can download the standalone Unity Application Block here.  I highly recommend trying out the EL because it has a lot of very useful application blocks for common needs of enterprise developers.

The benefits of using a more powerful framework such as Unity are that you get additional features such as simplified object creation and lifetime management (can register objects as singletons) in addition to the other benefits of looser coupling and more flexibility.  You can also use caching to store containers and then retrieve them when you need to access it again.

I will be using the same solution and project as my previous post and we will start by adding a reference to the Unity assembly.  The first method I will use is to manually register types with the unity container.  The second will be using configuration to specify the container’s configuration and types to use.  The ultimate bench mark for me will be to determine which method uses the least amount of code and leaves us with the most flexibility to make changes.

Manually Building and Registering Objects with the Container

Following the guidance in "Setting Up the Unity Container" I came up with this code:

Module Module3

    Sub Main()
        Dim container As Microsoft.Practices.Unity.IUnityContainer = _
        New Microsoft.Practices.Unity.UnityContainer

        container.RegisterType(Of Authenticator.IAuthenticator,  _
        Authenticator.DatabaseAuthenticator)()

        Dim authenticator As Authenticator.IAuthenticator = _
         container.Resolve(Of Authenticator.IAuthenticator)()
        Dim userName As String
        Dim password As String
        Dim user As Authenticator.User

        Console.WriteLine("Username: ")
        userName = Console.ReadLine

        Console.WriteLine("Password: ")
        password = Console.ReadLine

        Console.Clear()

        user = authenticator.Authenticate(userName, password)

        If user.IsAuthenticated Then
            Console.WriteLine(String.Format("User authenticated via {0}!", _
              user.AuthenticationType))
        Else
            Console.WriteLine("User authentication failed!")
        End If

        Console.ReadLine()
    End Sub

End Module

This was a pretty basic setup and the learning curve was almost zero.  I was already using a similar method to set up my own dependency injection and this was a very natural seeming approach.  However, I am not entirely satisfied with this approach as it has constrained us to making code changes and recompiling to switch to a new implementation of IAuthenticator. 

Now this was a very basic and simple example so I wouldn’t say this is a fault of Unity but more a fault of me just doing the bare minimum to create a container and add an object to it.

Configuring a Container Using Configuration Files

Let’s look at using Unity’s configuration schema.

The big disappointment I had so far is that there does not appear to be a visual configuration tool.  I know, how sad, but I do like having those tools available as I think they can help reduce little mistakes you can make when editing XML.  The broader EL configuration tool is very handy and now that I am used to the structures it creates for things like logging and validation it is very easy to use.

After reading "Entering Configuration Information" and some trial and error I developed Module4 which will use a configuration to define the container.

Module Module4

    Sub Main()
        Dim container As Microsoft.Practices.Unity.IUnityContainer = _
        New Microsoft.Practices.Unity.UnityContainer

        Dim section As _          Microsoft.Practices.Unity.Configuration.UnityConfigurationSection _
          = CType(Configuration.ConfigurationManager.GetSection("unity"),  _
          Microsoft.Practices.Unity.Configuration.UnityConfigurationSection)
        section.Containers.Default.Configure(container)

        Dim authenticator As Authenticator.IAuthenticator = _
         container.Resolve(Of Authenticator.IAuthenticator)()
        Dim userName As String
        Dim password As String
        Dim user As Authenticator.User

        Console.WriteLine("Username: ")
        userName = Console.ReadLine

        Console.WriteLine("Password: ")
        password = Console.ReadLine

        Console.Clear()

        user = authenticator.Authenticate(userName, password)

        If user.IsAuthenticated Then
            Console.WriteLine(String.Format("User authenticated via {0}!", _
              user.AuthenticationType))
        Else
            Console.WriteLine("User authentication failed!")
        End If

        Console.ReadLine()
    End Sub

End Module

After adding the required configSection the unity configuration block looks like so:

<unity>
    <typeAliases>
        <!-- User-defined type aliases -->
        <typeAlias alias="IAuthenticator"
             type="Authenticator.IAuthenticator, Authenticator" />
    </typeAliases>
    <containers>
        <container>
            <types>
                <!-- Type mapping using aliases defined above -->
                <type type="IAuthenticator"
                      mapTo="CustomAuthenticator.XmlAuthenticator,
                      CustomAuthenticator" />
            </types>
        </container>
    </containers>
</unity>

I used the alias feature to create an alias name for the IAuthenticator.  This lets you reference it by alias rather than including the full type information every time you are mapping an instance of the type.

In this example I rewrote the same program I was using for my simple dependency injection to use the Unity Application Block.  The code changes amounted to about 5 lines of code for the simple use of Unity and 6 to use configuration.  The code to use unity was very plain and had no real branching logic in it so it could easily be abstracted to a helper class that would assist in creating the container and returning the instance to the caller.

The flexibility that you can gain by moving to a IOC framework such as Unity is very impressive.  Overall I think this will be beneficial to use as a more robust way to enable dependency injection. 

Next time I will cover a more real world example using Unity in the adapter pattern we use for our WCF service clients.  The basic model is that our service client calls the service and then initializes an instance of a "IMessageAdapter" and calls its Transform method.  The purpose of the adapter is to take the data in the services format and transform or adapt it to something else like a database, fixed length file, etc.  We use this model because it allows us to fully reuse the client and only requires that we create a new implementation of the Interface.