Unity - Part 1: Introduction
Updated: ContainerControlled instead of ExternallyControlled in the bullet list of lifetime managers. Thanks, Ross Smith!
Introduction
Unity is Microsoft’s Inversion of Control (IoC) and Dependency Injection (DI) container. It is part of the Enterprise Library family of Application Blocks.
I have been using it for years. Yes, I am aware that there are lots of other tools for the same purpose and with more or less functionality, but I actually like Unity. It has a good combination of out of the box features, performance and extensibility options. Some of its aspects, I believe, are not well know, so I set out to write a couple of posts on it, this being the first. This will not be an introduction to IoC and DI, I am assuming you already know that.
First things first: the best way to install Unity is by using Nuget:
It will install both Unity and the Common Service Locator, and all .NET versions from 2.0 are supported.
The Common Service Locator is an attempt to define a common interface for a IoC functionality; different IoC libraries implement adapters for the Common Service Locator, so that they can be used through the common interface, without tying the implementation to a particular one. Most IoC containers already implement one such adapter.
We create a Unity container like this:
1: private static readonly IUnityContainer unity = new UnityContainer();
And we set up the Common Service Locator from it:
1: ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(unity));
An IoC container is no good without registrations. You can register entries on the configuration file or through code. An IoC container is particularly good for minimizing coupling, by having dependencies on interfaces and abstract base classes instead of actual implementations, so these registrations basically consist of an interface of abstract base class as the key and a concrete type or instance as the value. Let’s consider a simple interface, ILogger, and two concrete implementations, ConsoleLogger and FileLogger:
The implementation is not very important, but lets consider the following ILogger interface:
1: public interface ILogger
2: {
3: void Log(String message);
4: }
Registration By Configuration File
If you want to register by configuration, place the following content on the Web.config or App.config file:
1: <configuration>
2: <configSections>
3: <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
4: </configSections>
5: <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
6: <container>
7: <register type="MyNamespace.ILogger, MyAssembly" mapTo="MyNamespace.ConsoleLogger, MyAssembly"/>
8: <register type="MyNamespace.ILogger, MyAssembly" mapTo="MyNamespace.FileLogger, MyAssembly" name="File"/>
9: </container>
10: </unity>
11: </configuration>
Notice the two registrations, both for the interface ILogger, one mapping to ConsoleLogger, without a name, and another mapping to FileLogger, with a name of File. This is perfectly valid, there can be multiple declarations for the same interface (or abstract base class), provided they have different names. If no name is specified, it will default to the empty string (“”).
We have to tell Unity to load the configuration from the .config file, and we do it like this (do reference the Microsoft.Practices.Unity.Configuration namespace for the LoadConfiguration extension method):
1: unity.LoadConfiguration();
Registration By Code
Alternatively, if you want to register entries by code, do so as:
1: unity.RegisterType<ILogger, ConsoleLogger>();
2: unity.RegisterType<ILogger, FileLogger>("File");
With registration by code, you can do something that is not available with the configuration file approach: registering a concrete instance as the value. Here’s how:
1: unity.RegisterType<ILogger, ConsoleLogger>();
2: unity.RegisterInstance<ILogger>("File", new FileLogger());
This has the advantage that you can create an instance anyway you like, with any specific configuration, and make it available to Unity.
Pay attention to this: the Common Service Locator has no methods for adding entries to the registration, just for resolving them.
Getting Instances
After you have some entries registered, you can ask for them, either by calling Unity directly (not recommended) or through the Common Service Locator (better):
1: ILogger loggerFromUnity = unity.Resolve<ILogger>();
2: ILogger loggerFromCommonServiceLocator = ServiceLocator.Current.GetInstance<ILogger>();
Or, if you want to obtain a particular named registration:
1: ILogger fileLoggerFromUnity = unity.Resolve<ILogger>("File");
2: ILogger fileLoggerFromCommonServiceLocator = ServiceLocator.Current.GetInstance<ILogger>("File");
For dynamic invocation, when you don’t have at compile time the desired Type, and thus no strong typing:
1: ILogger loggerFromUnity = unity.Resolve(typeof(ILogger)) as ILogger;
2: ILogger loggerFromCommonServiceLocator = ServiceLocator.Current.GetInstance(typeof(ILogger)) as ILogger;
It is possible to get all registered values for a given type:
1: IEnumerable<ILogger> loggersFromUnity = unity.ResolveAll<ILogger>();
2: IEnumerable<ILogger> loggersFromCommonServiceLocator = ServiceLocator.Current.GetAllInstances<ILogger>();
3:
And, if there is the need, you can also resolve instances for a Type, that is, not using generics:
1: IEnumerable<ILogger> loggersFromUnity = unity.ResolveAll(typeof(ILogger)).OfType<ILogger>();
2: IEnumerable<ILogger> loggersFromCommonServiceLocator = ServiceLocator.Current.GetAllInstances(typeof(ILogger)).OfType<ILogger>();
For the single instance methods, if no such registration exists (for the type or the name), an exception will be thrown. For the enumeration ones, an empty list will be returned.
Lifetime Managers
By now, you might ask yourself: OK, but what if we ask Unity for some registered interface two or more times, will it give me the same instance or different instances? If we have registered an instance, by calling RegisterInstance instead of RegisterType, Unity will indeed return the same instance all the time. Otherwise, Unity will do what the Lifetime Manager tells it to do. For a good discussion, see Understanding Lifetime Managers.
A Lifetime Manager is an instance of a class that inherits from LifetimeManager and is responsible for creating or retrieving stored instances of mapped classes. Unity comes with the following out of the box implementations, but you are free to implement others:
-
ContainerControlledLifetimeManager (aka, singleton): Unity will only create an instance of the concrete type, and will return it every time, effectively treating it as a singleton;
-
PerThreadLifetimeManager (perthread): a different instance will be created once for each thread that the method it is called on, and the same instance will be returned on all subsequent calls for the same thread;
-
TransientLifetimeManager (transient): a new instance will be created and returned every time.
-
HierarchicalLifetimeManager (hierarchical): a new instance will be created and the same one will be returned for each Unity container, and another one for each child container (more on this later).
-
PerResolveLifetimeManager (perresolve): a new instance will be created every time, but it will be reused in the same build process if it is needed as a dependency somewhere in the list of dependencies of the current resolve process.
-
ExternallyControlledLifetimeManager (external): the actual instance was created and is managed elsewhere, like when you supply your own instance.
If we don’t specify a particular Lifetime Manager instance, or if we use null, the default is TransientLifetimeManager. The RegisterType method has overloads that take a LifetimeManager instance:
1: unity.RegisterType<ILogger, FileLogger>("File", new ContainerControlledLifetimeManager());
If you want to do it by configuration:
1: <register type="MyNamespace.ILogger, MyAssembly" mapTo="MyNamespace.FileLogger, MyAssembly" name="File">
2: <lifetime type="singleton"/>
3: </register>
Conclusion
That’s it for now. In the next post: dependency injection.