Click here to Skip to main content
15,887,289 members
Articles / Desktop Programming / WPF

Microsoft Unity

Rate me:
Please Sign up or sign in to vote.
4.66/5 (30 votes)
10 May 2015CPOL9 min read 46.8K   1.1K   44   5
An introductory look at Microsoft's Unity.

Introduction

This article provides an introductory look at Unity - Microsoft's Dependency Injection container.

What is Dependency Injection? And Why Is It A Good Idea?

Consider the classic situation of a business object that needs to be able to persist itself to a database. What is the best way to organise the code so that the business object can access the database?

The possibilities include:

  1. Hard code a connection string into the business object.
  2. Place the connection string in a configuration file and hard code the location of the configuration file into the object.
  3. Pass a locator interface to the business object in its constructor or via a property setter. The business objects asked the locator for access to the database.
  4. Pass a database interface to the business object in its constructor or via a property setter.

The first two options put hard-coded configuration information into the business object so are not recommended.

The third option is an example of the service locator pattern. If the business object uses the default service, the business object no longer contains any hard-coded configuration information. However some people regard the service locator pattern as an anti-pattern since (i) the business object may request a specific service by name, in which case the business object must know about its environment, and (ii) the .NET service locator can provide multiple service types so the business object's dependency on the database is hidden; the dependency is not apparent by looking at the object's method signatures - that makes refactoring difficult if one is not willing to trawl through the business object's source code.

The last option is an example of Dependency Injection. The database is a dependency of the business object, and the database is "injected" into the business object. Dependency Injection has the advantages that the business object is no longer concerned with any details about how to obtain a database object. Its sole purpose is to execute business logic. I.e. the design exhibits "Separation of Concerns" (components should do one thing) which is considered good programming practice. Also the business object's dependencies are declared in the constructor and so visible to the external world.

Where does the logic exist to create the database object and feed it to the business object? In a small application, the developer may simply craft some code that:

  1. looks up some configuration file for a connection string
  2. creates the database object, and
  3. creates the business object passing the database object to it in its constructor

The Unity framework replaces that hand crafted code with a configurable framework that uses declarative statements to define how objects should be created and initialized.

Inversion of Control

The developer's custom framework described in the previous section and Unity are both examples of the Inversion of Control (IoC) pattern. In both cases, the framework begins execution at start-up and business objects are instantiated by and used by the framework in response to events, i.e., the framework "controls" the business objects, which is the reverse of the "normal" flow of control.

Service Locator

A Unity container can be converted into a service locator by wrapping the container in a UnityServiceLocatorAdapter. Despite its status as an anti-pattern amongst some commentators, Prism uses the IServiceLocator interface so it is common to see code like the following in Prism/Unity applications.

C#
IUnityContainer container;

...

container.RegisterInstance<IServiceLocator>(new UnityServiceLocatorAdapter(container));

Registering and Resolving Types

The following code snippet uses an IOptionPricer interface obtained from the Unity framework to price an option.

The BinomialOptionPricer object is defined and registered with the UnityContainer so that whenever the container is asked for a IOptionPricer interface, the Unity container creates and returns an instance of a BonomialOptionPricer. (The IOptionPricer interface is said to be resolved to the type BinomialOptionPricer).

C#
// This is the interface requested from Unity and used to price the option.
interface IOptionPricer
{
    double Calculate(OptionType optionType, double S, double K, double T, double r, double vol);
}

// This is the object that will be actually returned by Unity
// (once registered) whenever an IOptionPricer is requested.
class BinomialOptionPricer : IOptionPricer
{
    ...
}

IUnityContainer container = new UnityContainer(); // Always use the IUnityContainer
                // rather than the underlying container.
container.RegisterType<IOptionPricer, BinomialOptionPricer>();   // Registered
                // the default implementation for IOptionPricer.

...

// Create a pricer () - request a IOptionPricer
IOptionPricer pricer = container.Resolve<IOptionPricer>();  // Returns an instance
                            // of BinomialOptionPricer.

// Use the pricer to price an option.
double PV = price->Calculate(OptionType.Call, S, K, T, r, vol);

Singletons

A Unity container can be configured using the IUnityContainer.RegisterInstance() method so that the same object (a singleton) is returned whenever a request is made for a specific interface.

C#
IOptionPricer pricer = new BinomialOptionPricer();  // This is always
            //returned when an IOptionPricer is requested.

container.RegisterInstance<IOptionPricer>(pricer);

Smart Injection

The code snippet below instantiates a OptionPriceFrm object. Note that the OptionPriceFrm constructor takes two parameters and so requires valid IRateProvider and IOptionPricer interfaces. Traditional code would require that objects be created as parameters before the form can be created, as below.

C#
public class OptionPricerFrm
{
    public OptionPricerFrm(IRatesProvider provider,
    IOptionPricer pricer)  // constructor takes parameter
    {
        ...
    }
    ...
}

IRatesProvider provider = new RatesProvider(...);  // create parameter 1.

IOptionPricer pricer = new OptionPricer(...);  // create parameter 2.

var frm = new OptionPricerFrm(provider, pricer);  // previously constructed objects passed to ctor.

Unity in contrast does not require us to provide parameter values for the constructor; it will recursively resolve any interfaces needed as parameters by the constructor. The Unity code to create the form would look like:

C#
// Unity figures out what parameters are needed to pass to the ctor and creates them.
var frm = container.Resolve<OptionPricerFrm>();

The Unity version of the code is much easier to refactor. (For example, if the OptionPriceFrm object was altered so that its constructor took an additional interface, none of the code to resolve IOptionPricer interfaces would need to be changed).

Unity also provides facilities to override the constructor parameters values, and to initialise resolved objects using properties. The reader is referred to Unity documentation (and the source code) for further details.

Named Registration

The same interface may refer to different underlying object types in different parts of an application. For example, a trading application may use an IOptionPricer interface to price options. A trader may want to use a non-standard model to hedge his positions since he feels that it more accurately reflects market prices, but use an approved model to calculate limits, charges and to report Profit and Loss to the Middle Office.

The following code shows two separate registrations for the IOptionPricer interface; the parameter supplied to the RegisterType method is the name of that registration.

C#
IUnityContainer container = new UnityContainer(); // Always use the
            // IUnityContainer rather than the underlying container.

container.RegisterType<IOptionPricer,
ApprovedOptionPricer>("std");        // Registered the approved option pricer.
container.RegisterType<IOptionPricer,
BinomialOptionPricer>("traders");       // Registered the non-standard binomial pricer.

The name of the registration to use to resolve the type is passed to the Resolve method as a parameter, as below.

C#
IUnityContainer container = new UnityContainer(); // Always use the
        // IUnityContainer rather than the underlying container.

var approvedPricer = container.Resolve<IOptionPricer>
    ("std");       // Registered the approved option pricer.

var tradersPricer = container.Resolve<IOptionPricer>
    ("traders");    // Registered the non-standard binomial pricer.

If no names is specified, the registration is referred to as the default registration.

Note that the registration name can be changed dynamically, even if the resolved objects have constructors that take a different number of parameters.

Using the Unity Configuration File

If we rely on the previous coding approach and want to use a different implementation class for IOptionPricer, we need to recompile the application.

A Unity container can be initialised from the app.config (or web.config) file. In the example below, the first part of the type and mapTo strings is the type name, the second part is the name of the assembly in which the object or interface resides. The section says there is a single Unity container named "main" with two mappings from the OptionPricersInterfaces.IOptionPricer interface - one to the QuantLib.OptionPricer object in the QuantLib assembly, and another to the TraderModels.BinomialOptionPricer object in the TraderModels assembly.

XML
<unity  xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <containers>
        <container name="main">

            <type name="Traders Screen"
                  type="OptionPricersInterfaces.IOptionPricer, OptionPricersInterfaces"
                  mapTo="TraderModels.BinomialOptionPricer, TradersModels"/>

            <type name="Profit and Loss Report"
                  type="OptionPricersInterfaces.IOptionPricer, OptionPricersInterfaces"
                  mapTo="QuantLib.OptionPricer, QuantLib"/>

        </container>
    </containers>
</unity>

The code to initialise the Unity container from the configuration file is shown below:

C#
IUnityContainer container = new UnityContainer();

// Use configuration file to register types.
UnityConfigurationSection configSection =
(UnityConfigurationSection)ConfigurationManager.GetSection("unity");
configSection.Configure(container, "main");

Modules

Microsoft provides a number of mechanisms to assist with the registration of interfaces and implementations including modules and the Unity bootstrapper. Modules are more low-level than the bootstrapper, which uses modules internally. Applications tend to use the UnityBootstrapper, however it is useful to know the steps involved if using modules directly (shown below). Note that modules are part of Prism, not Unity.

C#
// This is used by the ModuleManager so cannot be omitted.
container.RegisterInstance<IServiceLocator>(new UnityServiceLocatorAdapter(container));

// This is used by the ModuleManager so cannot be omitted.
TextLogger logger = new TextLogger(); // Default logger
container.RegisterInstance<ILoggerFacade>(logger);

//
// Setup the module catalog. Catalog specific logic goes here.
//
IModuleCatalog catalog =
new ModuleCatalog();  // Create the catalog - may be a derived type.
container.RegisterInstance<IModuleCatalog>
    (catalog);  // Singleton so IModuleManager can find it.
...

container.RegisterType<IModuleInitializer, ModuleInitializer>();

container.RegisterType<IModuleManager, ModuleManager>();

IModuleManager manager = container.Resolve<IModuleManager>();
manager.Run();

In every case, a ModuleCatalog or derived object must be setup so that it will be populated with ModuleInfo. There are several ModuleCatalog derived classes including ConfigurationModuleCatalog which populates the catalog from a configuration file, and DirectoryModuleCatalog which populates the catalog by scanning a directory for modules. Custom ModuleCatalog objects (which implement IModuleCatalog) are also possible. Once the catalog has been setup, the ModuleManager.Run loads and registers the specified types with Unity.

DirectoryModuleCatalog

The DirectoryModuleCatalog specifies a directory to scan at startup. The ModuleManager (reflection-only) loads any assemblies it finds and checks for objects that implement IModule. If one is found, an instance of the class (Registration in the example below) is created. The constructor (or IUnityContainer property) will be passed a IUnityContainer interface since it is needed to do the registration. Once the object is created, the IModule.Initialise() method is called which registers the interfaces and implementations with Unity.

C#
public class Registration : IModule
{
    IUnityContainer container = null;
    public Registration(IUnityContainer container)
    {
        this.container = container;
    }

    public void Initialize()
    {
        container.RegisterType<IOptionPricer, OptionPricer>("std");
        container.RegisterType<IOptionPricer,
            BinomialTreeOptionPricer>("binomial");
        container.RegisterType<IRatesProvider, RatesProvider>("std");
    }
}

ConfigurationModuleCatalog

The ConfigurationModuleCatalog which initialises Unity from the application (or web) configuration file is particularly simple to setup.

C#
ConfigurationModuleCatalog catalog = new ConfigurationModuleCatalog();
container.RegisterInstance<imodulecatalog>(catalog);

UnityBootstrapper

Microsoft provides the UnityBootstrapper class to simplify the coding of Prism applications that use Unity. It does a lot more than just initialise the Unity IoC container. The author thinks PrismBootstrapperForUnity would be a better name - the following processing occurs in the UnityBootstrapper.Run method:

  1. Creates a ILoggerFacade implementation.
  2. Creates a ModuleCatalog via CreateModuleCatalog.
  3. Creates a Unity container.
  4. Configures the default region adapter mappings.
  5. Configures default region behaviours
  6. Registers Prism framework exceptions.
  7. Creates the shell via CreateShell.
  8. Initialises the shell.
  9. Initialise the modules and registers interfaces and implementations with Unity.

The UnityBootstrapper is typically used by overloading the CreateModuleCatalog() and CreateShell() methods.

C#
class OptionPricerBootstrapper : UnityBootstrapper
{
    protected override IModuleCatalog CreateModuleCatalog()  // previously GetModuleCatalog()
    {
        string path = @".\Modules";
        if (!Directory.Exists(path))
            throw new Exception(path + " does not exist.");
        return (new DirectoryModuleCatalog() { ModulePath = path });
    }
    protected override System.Windows.DependencyObject CreateShell()
    {
        MainWindow wnd = new MainWindow(Container);
        wnd.Show();
        return wnd;
    }
}

Bootstrapper().Run();

The CreateModuleCatalog() is typically used to create a DirectoryModuleCatalog that points to the location of the self-registering modules (as above), but could just as easily return a ConfigurationModuleCatalog or custom ModuleCatalog.

The CreateShell() method expects the shell (main window) is created and returned by this method (the StartupUri in App.xml should be removed). Please refer to Prism documentation for the role of the shell in Prism (composite) applications.

Sample Code

The sample code consists of several projects:

  • OptionPricerInterfaces - This contains definitions of the IOptionPricer, IRateProvider and IOptionPricer interfaces used by all the other projects.
  • OptionPricers - Contains implementations for IOptionPricer, IRateProvider and IOptionPricer used by all the applications.
  • BareBones - A very simple Unity application to demonstate the basic idea of registration and type resolution. It is a command line application.
  • BareBones2 - Demonstrates some slightly more complex examples of the use of Unity. Also a command line application.
  • OptionPricerApp - A Winforms application that displays an option pricing dialog and registers and instantiates the OptionPricer object using Unity in code.
  • OptionPricerApp2 - A Winforms application that displays an option pricing dialog and uses Unity to resolve OptionPricerFrm, OptionPricer and RatesProvider objects. The OptionPricer interfaces/implementations are registered using a configuration file.
  • OptionPricerApp3 - A Winforms application that displays an option pricing dialog and uses a DirectoryModuleCatalog to initialise the Unity container by scanning a directory for modules.
  • OptionPricerApp4 - A WPF application that uses the Prism UnityBootstrapper.

Potential Issues with Unity

Large configuration files can become cumbersome to maintain - it can become difficult to spot errors. (The author has wasted quite a few hours this way). It is always very tempting to overuse "new" technology. There is no reason not to use Unity throughout your application, but it may be better to only move registration declarations to a configuration file if there is a real likelihood that the registered types will change. The author is aware that painful release procedures and internal IT development policies in large corporation may mean that advice is not realistic.

It may makes sense that each Use Case should have its own named interface registrations to avoid the situation where a change to one part of an application results in unexpected changes elsewhere.

Conclusion

Unity is a great, lightweight container that supports Dependency Injection. There is no obvious reason not to use it no matter what technology you are using, whether it is WPF, Winforms or something else.

History

No updates yet.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Bowmain
United Kingdom United Kingdom
Shaun O'Kane is a software developer with over 25 years experience. His interests include C++, C#, Windows, Linux, WPF, sockets, finance, .. just about everything.

Comments and Discussions

 
QuestionSample Code Download link? Pin
Paul_Williams14-May-15 22:52
Paul_Williams14-May-15 22:52 
Hi, thanks for an interesting and informative article. However I cannot find a link to download the sample code mentioned. It code appears to be there if I click on the 'Browse Code' side bar item, but not as a download link at the top of the page.
QuestionUnity vs. Ninject Pin
JoshYates198011-May-15 12:07
professionalJoshYates198011-May-15 12:07 
GeneralMy vote of 4 Pin
vytheese11-May-15 3:57
professionalvytheese11-May-15 3:57 
GeneralRe: My vote of 4 Pin
DriveByCoder11-May-15 6:30
professionalDriveByCoder11-May-15 6:30 
GeneralRe: My vote of 4 Pin
Dirk Bahle30-Jul-17 1:46
Dirk Bahle30-Jul-17 1:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.