IoC and the Unity Application Block Once Again
- IoC Containers, Unity and ObjectBuilder2 - The Saga Continues
- IoC Container, Unity and Breaking Changes Galore
Before I continue, check out David Hayden's screencast of IoC with ASP.NET MVC for some more interesting ideas regarding IoC and Unity.
Setter Injection versus Constructor Injection
I've been down the road of constructor injection versus setter injection. My opinion is that you try to inject the most you can through your constructor such as in a Model View Presenter, injecting the View and the Service Layer. Now, you may have cross-cutting concerns such as logging that you may want to swap out, but putting it in the constructor can just get ugly and you can set a default logger in your class anyways such as a NullLogger. Martin Fowler, as always, covers this topic here and it's quite interesting as it is an open debate.
The other option I've done down this road was to create an IContext interface which is injected through the constructor that has some of the cross-cutting concerns wrapped up into it, but for now, let's just walk through a sample of having setter injection as well.
Get Down to Details
Now that we got all the other stuff out of the way, let's go ahead and set up some basic examples in Unity, Castle Windsor and StructureMap doing setter injection. I'll basically take the code from last time and apply it here as well. So, let's start out with the Unity Application Block Sample:
namespace UnitySamples
{
public interface ILogger
{
void Log(string message);
}
}
And we have two implementations, the NullLogger (the default) and the ConsoleLogger (the override). Let's go through each of those.
namespace UnitySamples
{
public class NullLogger : ILogger
{
public void Log(string message)
{
// Do nothing
}
}
}
using System;
namespace UnitySamples
{
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
}
Now that I have these two implementations, I won't bother repeating these for the others unless they specifically change, which they shouldn't. Now, to my implementation details of my CustomerTasks object.
using Microsoft.Practices.Unity;
namespace UnitySamples
{
public class CustomerTasks
{
private ILogger logger = new NullLogger();
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public void SaveCustomer()
{
Logger.Log("Saved customer");
}
}
}
As you may note, I set the default implementation of my logger to a NullLogger because by default, I don't care about logging, so it's about the same as a NoOp. But, if I want to change that, I can through my setter injection. This goes for any cross-cutting concern. I also marked my Logger property with the DependencyAttribute which basically says that give me the ILogger instance registered when you go through the container to create it. This of course requires class modification, so it's not a simple rip and replace as it was with constructor injection. So, now let's look at the implementation of our configuration, much as before.
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);
CustomerTasks tasks1 = container.Resolve<CustomerTasks>();
CustomerTasks tasks2 = container.Resolve<CustomerTasks>();
tasks1.SaveCustomer();
tasks2.SaveCustomer();
}
}
}
And the configuration file will look much like before as:
<?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>
</container>
</containers>
</unity>
</configuration>
Now, let's do the same thing with Castle Windsor. Most of the code will be the same from our Unity sample as I can't imagine that much will change except that I don't need to register the DependencyAttribute on the CustomerTasks object. Let's first look at the changed CustomerTasks object.
namespace CastleSamples
{
public class CustomerTasks
{
private ILogger logger = new NullLogger();
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public void SaveCustomer()
{
Logger.Log("Saved customer");
}
}
}
Now, let's look at the program.cs file to see how really nothing changed at all from our perspective:
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
namespace CastleSamples
{
class Program
{
static void Main(string[] args)
{
WindsorContainer container = new WindsorContainer(new XmlInterpreter());
CustomerTasks tasks1 = container.Resolve<CustomerTasks>();
CustomerTasks tasks2 = container.Resolve<CustomerTasks>();
tasks1.SaveCustomer();
tasks2.SaveCustomer();
}
}
}
And then our config file looks 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="customertasks" type="CastleSamples.CustomerTasks, CastleSamples" lifestyle="transient" />
<component id="logger.console" service="CastleSamples.ILogger, CastleSamples" type="CastleSamples.ConsoleLogger, CastleSamples" lifestyle="singleton" />
</components>
</castle>
</configuration>
So, in reality, nothing changed from our constructor injection versus our setter injection implementation for Castle Windsor. That to me is pretty cool... But, now, let's move our attention to StructureMap. Let's walk through only the code that has changed. I'm only going to use the one with the StructureMap.config file and not the fluent interfaces just yet. I know more of the config file and not enough of the interfaces to be effective. Let's go through what really changed. In reality, everything is the same from the Castle Windsor implementation and the StructureMap one except for the Program.cs and the StructureMap.config file, so let's look at the Program.cs first.
using StructureMap;
namespace StructureMapSamples
{
class Program
{
static void Main(string[] args)
{
CustomerTasks tasks1 = ObjectFactory.GetInstance<CustomerTasks>();
CustomerTasks tasks2 = ObjectFactory.GetInstance<CustomerTasks>();
tasks1.SaveCustomer();
tasks2.SaveCustomer();
}
}
}
And to make sure our object is a singleton, let's make our StructureMap.config file and walk through the details:
<?xml version="1.0" encoding="utf-8" ?>
<StructureMap>
<PluginFamily Type="StructureMapSamples.ILogger" Assembly="StructureMapSamples" DefaultKey="Console">
<Interceptors>
<Interceptor Type="Singleton" />
</Interceptors>
<Plugin Assembly="StructureMapSamples" Type="StructureMapSamples.ConsoleLogger" ConcreteKey="Console" />
</PluginFamily>
<PluginFamily Type="StructureMapSamples.CustomerTasks" Assembly="StructureMapSamples" DefaultKey="CustomerTasks">
<Plugin Assembly="StructureMapSamples" Type="StructureMapSamples.CustomerTasks" ConcreteKey="CustomerTasks">
<Setter Name="Logger" Key="Console" />
</Plugin>
</PluginFamily>
</StructureMap>
If you're familiar with StructureMap, the config file should make sense to you as I'm creating an ILogger with the default implementation being the ConsoleLogger and then we make sure it is a singleton through the use of an interceptor. Then we also do a setter injection through our plugin of the CustomerTasks with setting the Logger property to the key of Console. Pretty simple and straightforward.
So, as you can see, they are very similar in nature, but attack the problem in different ways. That's the beauty of it all, just determine your needs and your programming style and then the pick of your IoC container should come into line with that. These are pretty simple and naive samples, but I just want to whet your appetite to open your mind and look for yourself at these containers for yourself.
AOP with PostSharp and Unity?
Gael Fraiteur recently posted on the altdotnet list about the new release of PostSharp. Gael blogged about that here. A couple of things intrigued me about it, including the license change to LGPL for the runtime and GPL for the code. This requires a commercial license should you want to redistribute it with your application. But, I'll leave that up to him and the lawyers to figure out.
But, let's get back to Unity for just a second. Gael recently announced on the Unity Application Block discussion list about including an extension to support PostSharp which you can find here. You can find the code in particular to do this, plus the Stoplight sample converted at GoogleCode here under PostSharp4Unity. Get your favorite SVN client and pull it down.
So, let's take my basic example from above and try to get it using the default constructors instead of the approach of calling the container to get ourselves an instance. A good approach is to keep the container out of your way and produce cleaner code. A downside of using IoC containers is that they can litter up your code. PostSharp4Unity allows us to create an extension for Unity and use our default constructors.
First, let's create a Unity container provider so that we can initialize our container programmatically. Create a UnityContainerProvider in your project that inherits from the IUnityContainerProvider. That code should look like this:
using Microsoft.Practices.Unity;
using PostSharp4Unity;
using UnitySamples;
namespace UnitySamples
{
public sealed class UnityContainerProvider : IUnityContainerProvider
{
private readonly IUnityContainer container;
public UnityContainerProvider()
{
this.container = new UnityContainer()
.RegisterType<ILogger, ConsoleLogger>(new ContainerControlledLifetimeManager());
}
public IUnityContainer CurrentContainer
{
get { return this.container; }
}
}
}
Now we also have to mark our assemblyinfo.cs with our default UnityProviderContainer, so open up your assemblyinfo.cs and put this info in there:
using PostSharp4Unity;
using UnitySamples;
[assembly: DefaultUnityContainerProvider(typeof(UnityContainerProvider))]
Ok, done and now we have to mark our class that we want to mark our configurable objects that we want to control with PostSharp. So, open CustomerTasks and modify it to this:
using Microsoft.Practices.Unity;
using PostSharp4Unity;
namespace UnitySamples
{
[Configurable]
public class CustomerTasks
{
private ILogger logger = new NullLogger();
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
public void SaveCustomer()
{
Logger.Log("Saved customer");
}
}
}
So, then our program.cs can look like this now:
namespace UnitySamples
{
class Program
{
static void Main(string[] args)
{
CustomerTasks tasks1 = new CustomerTasks();
CustomerTasks tasks2 = new CustomerTasks();
tasks1.SaveCustomer();
tasks2.SaveCustomer();
}
}
}
But, in order for PostSharp to do its magic, we need to make sure it's part of our compilation targets in our .csproj file. So, I hand munged mine and got it to work like this:
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(PostSharpDirectory)PostSharp.targets" Condition=" Exists('$(PostSharpDirectory)PostSharp.targets') " />
</Project>
Then I'm able to get the magic of PostSharp to do its work. Pretty slick!
Wrapping It Up
So, I hopefully whetted your appetite for these IoC containers for you to go and do a comparison on your own. Each container has a different way of configuring itself and each solves a different problem set. It's up to you to decide which one to use. Along the way you can have a pretty cool journey though in learning deciding what works for you and what doesn't. Until next time, and hopefully an F# related post...