IoC comparison: Autoregistration in Microsoft Unity
The Microsoft Unity container does not include autoregistration, so you could do without it, roll your own or use the Unity Auto Registration addon by Artem Govorov. This post looks at Unity with this addon. Like Windsor's autoregistration API, it also uses a fluent interface. Unity's fluent interface starts with the extension method ConfigureAutoRegistration, then follows one or more Include(FilterPredicate, Then.Register.As… ) blocks, and ends with ApplyAutoRegistration();
Unity autoregistration has options to register interfaces, using AsFirstInterfaceOfType(), AsAllInterfacesOfType() and AsSingleInterfaceOfType(), which will throw an exception if more than one interface is found on a type. I did an AsSelf extension for registering concrete types. This is trivial, it is an extension method on IFluentRegistration with method body return registration.As(t => t).
Note that Unity does automatic resolution – so unregistered types can be resolved without being registered. However autoregistration does not apply any options to these types.
e.g. in this code which scans an assembly and registers all interfaced types as singletons:
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => t.Assembly == typeof(BusinessProcess).Assembly,
Then.Register().AsAllInterfacesOfType().UsingSingletonMode()).
ApplyAutoRegistration();
BusinessProcess businessProcess = container.Resolve<BusinessProcess>()
The type BusinessProcess is resolved, but is not a singleton. It has no interfaces, so has not been autoregistered at all, but is resolved automatically using default settings as a transient object. You may have to watch out for this – just because a concrete type has been resolved does not mean that it has been correctly configured using autoregistration.
You can apply different registration options to different components, but I haven't found a better way to do it than using different Include predicates:
container.ConfigureAutoRegistration().
Include(t => t != typeof(BusinessProcess),
Then.Register().AsAllInterfacesOfType()).
Include(t => t == typeof(BusinessProcess),
Then.Register().AsSelf().UsingSingletonMode()).
I think I prefer Windsor's system of ConfigureFor<> followed by a Configure<> that sets defaults on everything else; that way it’s harder to get it wrong. With Unity two different predicates are specified. I made them exact opposites, but I see this as a potential site for errors if they overlap or miss something out.
Unity autoregistration scans all currently loaded assemblies. During this, you can load an assembly by specifying the assembly name in a string, which will load it into the current AppDomain.
I don’t usually speak about speed. Most IoC containers are fast enough for most uses, and measuring their speed in resolving instances is not that interesting - it is usually done because it’s easy to measure, not because it’s important to measure.
However Unity is noticeably slower to register types than the others. My autoregistration tests on 5 containers take around 2.4 seconds to run in total. Of that, Unity takes 1.0 seconds. The others take around 0.3 second each. So unity is 41% of the total run time. I suspect that this is because it runs through all types in loaded assemblies, including the System.* namespaces. This tests registers 6 types. However, if the delay is due to scanning, registering more types during this scan will not slow it down.
But adding more assemblies will slow it down, if they are your code, other parts of the .Net framework, or third party toolkits. This makes me slightly uneasy - it seems as if changes in unrelated parts of the application can affect the speed of autoregistration. Hopefully you can use something like my InTargetAssembly() helper to make sure that unrelated assemblies don't actually affect what is autoregistered!
In conclusion, in general unity autoregistration works, but has some drawbacks compared to autoregistration in Castle Windsor.
I made some extension methods to aid autoregistration in unity. This was straightforward, and the code is included below. The code is maintained in the githib repository here and the extensions are here.
namespace IoCComparison.AutoregisterTests
{
using System;
using System.Linq;
using System.Reflection;
using AutoregisteredClasses.Interfaces;
using AutoregisteredClasses.Services;
using AutoregisteredClasses.Validators;
using IoCComparison.AutoregisterTests.UnityExtensions;
using Unity.AutoRegistration;
using Microsoft.Practices.Unity;
using NUnit.Framework;
/// <summary>
/// using the "Unity Auto Registration" addon http://autoregistration.codeplex.com/
/// </summary>
[TestFixture]
public class UnityTest
{
private static bool InTargetAssembly(Type type)
{
return type.Assembly == typeof(BusinessProcess).Assembly;
}
private static bool IsLibraryAssembly(Assembly assembly)
{
return
// NUnit is a 3rd party library
(assembly == (typeof(TestFixtureAttribute).Assembly)) ||
// Unity is a 3rd party library
(assembly == (typeof(UnityContainer).Assembly)) ||
// Spring
assembly.FullName.StartsWith("Spring") ||
assembly.FullName.StartsWith("Microsoft");
}
[Test]
public void CanMakeBusinessProcessWithAssemblyInclusion()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
// include just the target assembly.
Include(t => InTargetAssembly(t),
Then.Register().AsAllInterfacesOfType()).
ApplyAutoRegistration();
BusinessProcess businessProcess = container.Resolve<BusinessProcess>();
Assert.IsNotNull(businessProcess);
}
[Test]
public void CanMakeBusinessProcessWithAssemblyExclusion()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => true,
Then.Register().AsAllInterfacesOfType()).
// exclude system and library assemblies
ExcludeSystemAssemblies().
ExcludeAssemblies(a => IsLibraryAssembly(a)).
ApplyAutoRegistration();
BusinessProcess businessProcess = container.Resolve<BusinessProcess>();
Assert.IsNotNull(businessProcess);
}
[Test]
public void CanMakeSingletonInstance()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => t != typeof(BusinessProcess),
Then.Register().AsAllInterfacesOfType()).
Include(t => t == typeof(BusinessProcess),
Then.Register().AsSelf().UsingSingletonMode()).
// exclude system and library assemblies
ExcludeSystemAssemblies().
ExcludeAssemblies(a => IsLibraryAssembly(a)).
ApplyAutoRegistration();
BusinessProcess businessProcess1 = container.Resolve<BusinessProcess>();
BusinessProcess businessProcess2 = container.Resolve<BusinessProcess>();
Assert.AreSame(businessProcess1, businessProcess2);
}
[Test]
public void CanMakeTransientInstance()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => true,
Then.Register().AsAllInterfacesOfType()).
// exclude system and library assemblies
ExcludeSystemAssemblies().
ExcludeAssemblies(a => IsLibraryAssembly(a)).
ApplyAutoRegistration();
BusinessProcess businessProcess1 = container.Resolve<BusinessProcess>();
BusinessProcess businessProcess2 = container.Resolve<BusinessProcess>();
Assert.AreNotSame(businessProcess1, businessProcess2);
}
[Test]
public void CanMakeTransientInstanceWithSingletonDependencies()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => t != typeof(BusinessProcess),
Then.Register().AsAllInterfacesOfType()
.UsingSingletonMode()).
Include(t => t == typeof(BusinessProcess),
Then.Register().AsSelf()).
// exclude system and library assemblies
ExcludeSystemAssemblies().
ExcludeAssemblies(a => IsLibraryAssembly(a)).
ApplyAutoRegistration();
BusinessProcess businessProcess1 = container.Resolve<BusinessProcess>();
BusinessProcess businessProcess2 = container.Resolve<BusinessProcess>();
Assert.AreNotSame(businessProcess1, businessProcess2);
Assert.AreSame(businessProcess1.CustomerService, businessProcess2.CustomerService);
Assert.AreSame(businessProcess1.OrderService, businessProcess2.OrderService);
}
[Test]
public void CanGetAllValidators()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => InTargetAssembly(t),
Then.Register().AsAllInterfacesOfType()).
Include(If.Implements<IValidator>, Then.Register().As<IValidator>().WithTypeName()).
ApplyAutoRegistration();
var validators = container.ResolveAll<IValidator>();
Assert.IsNotNull(validators);
Assert.AreEqual(3, validators.Count());
}
[Test]
public void CanFilterOutValidatorRegistrations()
{
UnityContainer container = new UnityContainer();
container.ConfigureAutoRegistration().
Include(t => InTargetAssembly(t),
Then.Register().AsAllInterfacesOfType()).
Exclude(If.Is<FailValidator>).
Include(If.Implements<IValidator>, Then.Register().As<IValidator>().WithTypeName()).
ApplyAutoRegistration();
// excluding the FailValidator should leave 2 of them
var validators = container.ResolveAll<IValidator>();
Assert.IsNotNull(validators);
Assert.AreEqual(2, validators.Count());
}
}
}