IoC Containers, Unity and ObjectBuilder2 - The Saga Continues

Update:  Fixed code changed from CTP and More in the series:

I just wanted to revisit the whole Unity Application Block just once more to look at a few more things including handling parameters, instances and so on.  If you hadn't seen, there was another source drop of Unity as of 2/26, so if you haven't picked it up, feel free to do so here

So, we're going to continue our look at DI and IoC containers as they pertain to different needs.  I'm not going to be a gloryhound about any particular IoC container, instead, just lay out how each one does its job and you decide whether you want to use it or not.  And that's the main point I want to emphasize here that each tool has its purpose and its needs that it solves and it's up to you do decide whether it meets your needs and programming style.

Where We Are

Anyhow, I covered the Unity Application Block in a few other posts lately as it relates to other Inversion of Control (IoC) containers.  I covered the basics with the Unity Application Block, StructureMap and Castle Windsor.  Check them out before we go any further today:
Now that we're caught up to where we have been, let's dive into the other subjects in the same area of concern.  Unity has an interesting way of dealing with several issues that I want to cover in respect to other IoC containers.  And they are:
  • Managing Instances and Parameters
  • Method Call Injection
  • Unity Application Block versus ObjectBuilder2
Managing Instances or Parameters?

The previous two times, we talked about constructor injection and setter injection, but not really about managing simple types and mapping them to our constructors.  It's usually a simple thing, but yet, we have three different ways of attacking the very same problem.  That's the very point of these blog posts to point that out and it's once again echoed here.

To set up this scenario, we have a simple SmtpEmailService which is used to send emails to users in our system.  It's a pretty naive sample, yet will illustrate some simple constructor injection with both simple and complex types.  We're going to inject a default from address and a logger into our service and go from there.

We'll start with our Unity sample of how we are going to map those simple and complex types to the constructor.  Let's start off with the basic logger interface and implementation.  I'll only post this part once as it will apply the same to each of the samples.

namespace UnitySamples

{

    public interface ILogger

    {

        void Log(string message);

    }

}


And once again, we have our concrete implementation with the console logger.

using System;

 

namespace UnitySamples

{

    public class ConsoleLogger : ILogger

    {

        public void Log(string message)

        {

            Console.WriteLine(message);

        }

    }

}


Now, let's tie it together with the point of today's post, the SmtpEmailService.  We will have two parameters in this scenario, the default email address, and the logger used for logging.

using Microsoft.Practices.Unity;

 

namespace UnitySamples

{

    public class SmtpEmailService : IEmailService

    {

        private readonly string defaultFromAddress;

        private readonly ILogger logger;

 

        public SmtpEmailService([Dependency("defaultFromAddress")] string defaultFromAddress, ILogger logger)

        {

            this.defaultFromAddress = defaultFromAddress;

            this.logger = logger;

        }

 

        public void SendEmail(string to, string subject, string body)

        {

            // Send email

            logger.Log(string.Format("Sending email from {0} to {1} with the subject {2} and body {3}", defaultFromAddress, to, subject, body));

        }

    }

}


As you may note, I'm using the DependencyAttribute to decorate the parameter defaultFromAddress.  This correlates to the key specified in the config file or when you register an instance, and I'll show you that later.  
Let's take a look at the program.cs to use the XML config file to wire up our instances.

using System.Configuration;

 

using Microsoft.Practices.Unity;

using Microsoft.Practices.Unity.Configuration;

 

namespace UnitySamples

{

    class Program

    {

        static void Main(string[] args)

        {

            IUnityContainer container = new UnityContainer();

            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

            section.Containers.Default.GetConfigCommand().Configure(container);

            SmtpEmailService service = container.Resolve<SmtpEmailService>();

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


The above code is pretty standard as we've used before, so real surprises here.  So, let's take a look at the XML configuration necessary to make this work:

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

<configuration>

    <configSections>

        <section name="unity"

                type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,

                  Microsoft.Practices.Unity.Configuration" />

    </configSections>

    <unity>

        <containers>

            <container>

                <types>

                    <type type="UnitySamples.ILogger,UnitySamples"

                          mapTo="UnitySamples.ConsoleLogger,UnitySamples"

                          lifetime="Singleton"/>

                </types>

                <instances>

                    <add name="defaultFromAddress" type="System.String" value="administrator@example.com" />

                </instances>

            </container>

        </containers>

    </unity>

</configuration>


What I did is that I utilized the instances node and then add an instance called defaultFromAddress.  This node is only used to initialize simple types such as strings, DateTimes, System.Doubles, System.Int32 and so on.  Then I can set the value as well.  So, if I want to initialize complex types in the types node and the simple types in the instances node.

But, we can also register things through the regular programmatic interfaces as well such as this:

using Microsoft.Practices.Unity;

 

namespace UnitySamples

{

    class Program

    {

        static void Main(string[] args)

        {

            IUnityContainer container = new UnityContainer();

            container

                .RegisterType<ILogger, ConsoleLogger>(new ContainerControlledLifetimeManager())

                .RegisterInstance<string>("administrator@example.com", "defaultFromAddress");

            SmtpEmailService service = container.Resolve<SmtpEmailService>();

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


As you note, I used the fluent interfaces approach to register the ConsoleLogger as well as the instance of type string which had my value and the key I wanted to use.  The first value I use is the value, and the second is the key I want to use to correlate to my DependencyAttribute.

Now, let's look at the StructureMap instance using the same approach.  The only pieces I intend to show are just the pieces I changed.  So, let's take a look at the SmtpEmailService once again.

namespace StructureMapSamples

{

    public class SmtpEmailService : IEmailService

    {

        private readonly string defaultFromAddress;

        private readonly ILogger logger;

 

        public SmtpEmailService(string defaultFromAddress, ILogger logger)

        {

            this.defaultFromAddress = defaultFromAddress;

            this.logger = logger;

        }

 

        public void SendEmail(string to, string subject, string body)

        {

            // Send email

            logger.Log(string.Format("Sending email from {0} to {1} with the subject {2} and body {3}", defaultFromAddress, to, subject, body));

        }

    }

}


As you may note from above, I had no need for the dependency attribute or anything like that.  I haven't bothered to put the PluginAttributes or anything like that either as I want to do XML config way.  So, let's next look at the program.cs which does the simple email wireup. 

using StructureMap;

 

namespace StructureMapSamples

{

    class Program

    {

        static void Main(string[] args)

        {

            SmtpEmailService service = ObjectFactory.GetInstance<SmtpEmailService>();

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


And then we wire things up with our StructureMap.config file.  Note that we set up things through an Instance node and then set the constructor injection parameters through property nodes.

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

<StructureMap>

    <Aseembly Name="StructureMapSamples"/>

  <PluginFamily Type="StructureMapSamples.ILogger" Assembly="StructureMapSamples" DefaultKey="Console" Scope="Singleton">

    <Plugin Assembly="StructureMapSamples" Type="StructureMapSamples.ConsoleLogger" ConcreteKey="Console" />

    </PluginFamily>

  <PluginFamily Type="StructureMapSamples.SmtpEmailService" Assembly="StructureMapSamples" DefaultKey="EmailService">

      <Plugin Assembly="StructureMapSamples" Type="StructureMapSamples.SmtpEmailService" ConcreteKey="SmtpEmailService" />

      <Instance Key="EmailService" Type="SmtpEmailService">

          <Property Name="defaultFromAddress" Value="administrator@example.com" />

          <Property Name="logger" Key="Console" />

      </Instance>

  </PluginFamily>

</StructureMap>


I tidied up the XML configuration a bit after taking Jeremy Miller's advice and trying to collapse it down as much as possible.  I'm sure I still don't have it to the smallest amount possible, but at least it's legible.  Now, let's go ahead and use the DSL for configuration instead of the simple program.cs we have above.

using StructureMap;

using StructureMap.Configuration.DSL;

 

namespace StructureMapSamples

{

    class Program

    {

        static void Main(string[] args)

        {

            StructureMapConfiguration.UseDefaultStructureMapConfigFile = false;

            StructureMapConfiguration.BuildInstancesOf<ILogger>()

                .TheDefaultIsConcreteType<ConsoleLogger>().AsSingletons();

            StructureMapConfiguration.BuildInstancesOf<SmtpEmailService>().TheDefaultIs(

                Registry.Instance<SmtpEmailService>().UsingConcreteType<SmtpEmailService>()

                    .WithProperty("defaultFromAddress").EqualTo("administrator@example.com"));

            SmtpEmailService service = ObjectFactory.GetInstance<SmtpEmailService>();

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


As you can see from above, it's a pretty simple thing to use the more fluent interfaces here as well to register what we're interested in.  We're making sure it doesn't use the default StructureMap.config file and then build up an instance of an ILogger as a singleton, and then use the injection on the SmtpService to set the defaultFromAddress.  The ILogger it should automatically figure out itself.

Now, let's finally move onto Castle Windsor.  I must admit, I'm most familiar with Castle Windsor, but I'm learning something new about each of these every day, which is a good thing.  So, the only classes I really need to show is the program.cs and the configuration file to show what has changed.  So, let's start with the program.cs file:

using Castle.Windsor;

using Castle.Windsor.Configuration.Interpreters;

 

namespace CastleSamples

{

    class Program

    {

        static void Main(string[] args)

        {

            WindsorContainer container = new WindsorContainer(new XmlInterpreter());

            SmtpEmailService service = container.Resolve<SmtpEmailService>();

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


This is a pretty simple example of using the XmlInterpreter which reads the XML configuration file to read what we want to set.  And then our XML configuration file should look something like this:

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

<configuration>

    <configSections>

        <section name="castle"

            type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

    </configSections>

    <castle>

        <components>

            <component id="emailservice.smtp"

                type="CastleSamples.SmtpEmailService, CastleSamples">

            <parameters>

                <defaultFromAddress>administrator@example.com</defaultFromAddress>

            </parameters>

        </component>

        <component id="logger.console"

                       service="CastleSamples.ILogger, CastleSamples"

                       type="CastleSamples.ConsoleLogger, CastleSamples"

                       lifestyle="singleton" />

        </components>

    </castle>

</configuration>


I could have also specified the logger as well as a parameter by noting it inside the parameters node such as this:
<logger>${logger.console}</logger> to specify that it uses the ConsoleLogger, but it's not really necessary to do so.

Instead, let's use the XML free way of configuration just to prove a point we can do it both ways:

using System.Collections.Generic;

using Castle.Core;

using Castle.Windsor;

 

namespace CastleSamples

{

    class Program

    {

        static void Main(string[] args)

        {

            WindsorContainer container = new WindsorContainer();

            container.AddComponentWithLifestyle("logger.console", typeof (ILogger), typeof (ConsoleLogger),

                                                LifestyleType.Singleton);

            Dictionary<string, object> properties = new Dictionary<string, object>();

            properties.Add("defaultFromAddress", "administrator@example.com");

            container.AddComponent("emailservice.smtp", typeof (SmtpEmailService));

 

            SmtpEmailService service = container.Resolve<SmtpEmailService>(properties);

            service.SendEmail("me@example.com", "Test email", "This is a test email");

        }

    }

}


So, as you can see, it's pretty simple to set the parameters on our SmtpEmailService through the use of a Dictionary<TKey, TValue> generic collection.  The rest is just plain simple.

Method Call Injection

The Unity Application Block introduces another concept called method call injection.  Yes, that's right, instead of using constructor injection or setter injection, you can use method call injection.  The basic premise is that you can inject any method as you would for a constructor or setter.

When would you use this though?  If you read the documentation, it lays out the explanation as follows:
  • You want to instantiate dependent objects automatically when your instantiate the parent object
  • You want a simple approach that makes it easy to see in the code what the dependencies are for each class 
  • The parent object requires a large number of constructors that forward to each other, making debugging and maintenance difficult 
  • The parent object constructors require a large number of parameters, especially if they are of similar types and the only way to identify them is by position 
  • You want to hide the dependent objects by not exposing them as properties 
  • You want to control which objects are injected by editing the code of the dependent object instead of the parent object or application
By default unless any of this matches your criteria, you should just go ahead and use constructor injection.  But, let's walk through a small sample of using method injection:

using Microsoft.Practices.Unity;

 

namespace UnitySamples

{

    public class SmtpEmailService : IEmailService

    {

        private string defaultFromAddress;

        private ILogger logger;

 

        [InjectionMethod]

        public void Initialize([Dependency("defaultFromAddress")] string defaultFromAddress, ILogger logger)

        {

            this.defaultFromAddress = defaultFromAddress;

            this.logger = logger;

        }

 

        public void SendEmail(string to, string subject, string body)

        {

            // Send email

            logger.Log(string.Format("Sending email from {0} to {1} with the subject {2} and body {3}", defaultFromAddress, to, subject, body));

        }

    }

}


The rest of our program shouldn't have to change with this scenario.  But, as you will note from above, we decorated our Initialize method with an InjectionMethodAttribute which makes it realize that instead of constructor injection, use this instead.  And it works like a charm.  I haven't run into a scenario yet where I would find it useful, but I find it interesting.  If anyone does have a scenario that they've run into, please let me know.

Unity == ObjectBuilder2?

As you may have noticed, if you had downloaded the Unity Application Block, you'll notice that it is built upon ObjectBuilder2, but you may also notice that it hides a lot of functionality that it provides.  When you are marking your classes with DependencyAttribute and other things, you will notice that there are two of them, one in Unity and one in ObjectBuilder2, so this got me curious on what else I was missing out on.

If you've played around with Enterprise Library in the past, you have probably dealt with ObjectBuilder.  It was not the most delightful piece of code I've ever had to deal with and not very intuitive.  Brad Wilson and Scott Densmore took it upon themselves to rewrite this into a much more usable piece of code.  You can download the original and unencumbered ObjectBuilder2 from CodePlex here.

Take a look at the CodePlex DependencyInjection project and its associated tests in Tests.CodePlex.DependencyInjection for a good idea on how well Brad and Scott did with their DI framework.  The best way to learn about any framework is to read the tests or specs (BDD style of course).  Let's look at two ways of doing code interception using the DI framework with using code and using attributes.

            [Fact]

            public void InterceptViaAttributes()

            {

                Recorder.Records.Clear();

                DependencyContainer container = new DependencyContainer();

                container.RegisterTypeMapping<ISpy, SpyInterfaceAttributes>();

 

                ISpy obj = container.Get<ISpy>();

                obj.InterceptedMethod();

 

                Assert.Equal(3, Recorder.Records.Count);

                Assert.Equal("Before Method", Recorder.Records[0]);

                Assert.Equal("In Method", Recorder.Records[1]);

                Assert.Equal("After Method", Recorder.Records[2]);

            }

 

            [Fact]

            public void InterceptViaCode()

            {

                Recorder.Records.Clear();

                DependencyContainer container = new DependencyContainer();

                container.RegisterTypeMapping<ISpy, SpyInterface>();

                container.InterceptInterface<SpyInterface>(typeof(ISpy).GetMethod("InterceptedMethod"),

                                                           new RecordingHandler());

 

                ISpy obj = container.Get<ISpy>();

                obj.InterceptedMethod();

 

                Assert.Equal(3, Recorder.Records.Count);

                Assert.Equal("Before Method", Recorder.Records[0]);

                Assert.Equal("In Method", Recorder.Records[1]);

                Assert.Equal("After Method", Recorder.Records[2]);

            }


Ok, so you may note that we registered this with an ISpy interface.  Then we have two implementations, one with code and one using attributes.  Let's now inspect that code.

            public interface ISpy

            {

                void InterceptedMethod();

                void ThrowsException();

            }

 

            internal sealed class SpyInterface : ISpy

            {

                public void InterceptedMethod()

                {

                    Recorder.Records.Add("In Method");

                }

 

                public void ThrowsException()

                {

                    Recorder.Records.Add("In Method");

                    throw new Exception();

                }

            }

 

            internal sealed class SpyInterfaceAttributes : ISpy

            {

                [InterfaceIntercept(typeof(RecordingHandler))]

                public void InterceptedMethod()

                {

                    Recorder.Records.Add("In Method");

                }

 

                public void ThrowsException()

                {

                    throw new Exception();

                }

            }

        }


As you may note, we have two methods that we're interested in, the InterceptedMethod and the ThrowsException method.  Then we can test through a Recorder that in fact the code was hit.  xUnit.net is used quite frequently throughout the code as it is a project also by Brad and Jim Newkirk.  Overall, I'm very impressed with this code and I wish it had made more of an impact.

Chris Tavares and others from the Unity team have taken it upon themselves to redo a lot of the guts of ObjectBuilder2 as they continue to extend Unity.  For AOP, the plan is to include the Policy Injection Block into the solution, whereas ObjectBuilder2 already had this piece of functionality and does it quite well.  Some of it seems to be right now a little less featured than ObjectBuilder2 at this point, but that can change.  I have hopes for Unity, but ObjectBuilder2 did a lot of these things that it claims to do, just a little bit better.

Wrapping it Up

Here wraps up another installment of looking at IoC containers and the various frameworks associated with it.  I hope that it whets your appetite to not only understand these containers, but see how they work deep down and see which matches your programming style.  Get to know your tools just a little bit better and find out what good quality code can be.  I hope you can take this to heart and see what works for you.

kick it on DotNetKicks.com

5 Comments

Comments have been disabled for this content.