left-icon

Microsoft Unity Succinctly®
by Ricardo Peres

Previous
Chapter

of
A
A
A

CHAPTER 2

Inversion of Control (IoC)

Inversion of Control (IoC)


Introduction

Inversion of Control (IoC) is a software design pattern in which the developer does not explicitly specify the concrete class that will be instantiated; instead, the developer asks for an abstract base class or an interface that defines the functionality he or she wants. This increases decoupling by allowing the actual concrete class to be changed at a later stage or be dynamically selected based on some criteria. You are not tied to a particular implementation but, instead, rely on your system to provide you a proper one (you have the control without actually needing to care what it is).

So, instead of:

var logger = new ConsoleLogger();

You will have:

var logger = unity.Resolve<ILogger>();

Of course, you have to tell Unity how to do that. The following sections talk about precisely that.

Setup

First things first: You need to have an instance of the Unity container in your code. Save it in an instance or static field in your bootstrap class:

internal static readonly IUnityContainer unity = new UnityContainer();

That’s all it takes. The IUnityContainer interface defines the contract of the Unity container, which is itself implemented by the UnityContainer class. Do change the visibility to match your requirements but try to keep it as restricted as possible. You will, most likely, need only a single instance of the Unity container. Later on we’ll see a pattern for accessing Unity in an indirect, more decoupled way (

Tip: Don’t forget that Dispose is only called for some lifetime managers.

Since IUnityContainer implements IDisposable, it is a good principle to always create child containers in a using block so that they are properly disposed of when no longer needed.

A child container exposes the same interface as the parent—IUnityContainer—which has a Parent property but does not keep track of its children.

A typical scenario in which you would use a child container would be in a web application request. We’ll look at that when we talk later about Unity’s integration with Introduction

A number of technologies in the Microsoft stack use IoC. In fact, its number seems to be growing every time a new version or technology is launched. It is generally possible to plug in an IoC container of choice; in our case, let’s see how to use Unity.

ASP.NET Web Forms.

Common Service Locator).

It is important to keep in mind that the Unity container, depending on its configuration (more on this in section Lifetime Managers) may dispose of the registered types automatically; for this to happen, you must explicitly dispose of it. In this case, it might make more sense to have something like:

using (var unity = new UnityContainer())

{

    //program goes here

}

Domain Model

For the purpose of our conversation, we will use a simple class model:

Logger class model

Figure 3: Logger class model

Here we have an ILogger interface that defines our logging contract and two concrete implementations: ConsoleLogger, which logs to the console (would you know?) and FileLogger, which logs to a file in the disk. Let’s forget about the actual implementation of these classes; it’s not really important.

Registration by Code

Instance vs. Type

We need to tell Unity which concrete classes (components) we want to register and through which interfaces or abstract base classes they can be accessed (keys). Remember, the whole point of IoC is not knowing beforehand which concrete class is to be returned. The concept is that we register a concrete class under one of its base types or implementing interfaces. This is a requirement; the component class must be convertible to its key. If you are to register a FileLogger, you need to use as its key one of its base classes (such as Object, which is not really useful) or one of the interfaces that it implements (in this case, ILogger, which is what we want but could also be IDisposable, which is implemented by ILogger).

The IUnityContainer interface exposes some methods that allow us to register dynamic (untyped) components by code and there are also some extension methods for strongly-typed components that wrap the former in the UnityContainerExtensions class. The generic extension method allows for writing compile-time safe code and should generally be preferred. In the end, it all comes down to two methods:

  • RegisterInstance: Takes an existing instance of a class and makes it available through a base class or an interface that the class exposes.
  • RegisterType: Associates some base type (class or interface) with a concrete type.

Some of these methods take a name but those who don’t just assume a null name. It is possible to have a type registered several times but with different names:

unity.RegisterType<ILoggerConsoleLogger>("Console");

unity.RegisterType<ILoggerFileLogger>("File");

unity.RegisterInstance<ILogger>(new ConsoleLogger());

What we have here is multiple strong-typed registrations for the ILogger type:

  • One that maps type ILogger to type ConsoleLogger, with a name of Console,
  • Another that maps ILogger to FileLogger, with a name of File (how predictable),
  • And finally, a mapping of ILogger to a concrete instance of a ConsoleLogger, without a name.

We can also do that dynamically:

unity.RegisterType(typeof(ILogger), typeof(ConsoleLogger), "Console");

unity.RegisterType(typeof(ILogger), typeof(FileLogger), "File");

unity.RegisterInstance(typeof(ILogger), new ConsoleLogger());

Note: Remember that a registration is composed of a base type (class or interface) and a name. Every subsequent registration of a type with the same name—or lack of it—overwrites the previous one.

Tip: By default, only types with public, parameterless constructors can be used with RegisterType. Later on we’ll see how to go around this limitation by having Unity inject dependencies automatically.

Of course, it is also possible to register the same concrete type or instance several times for different keys. The type just needs to inherit from or implement all of them:

Type registrations inside Unity

Figure 4: Type registrations inside Unity

Why would you use RegisterInstance instead of RegisterType? Well, imagine that you want to store an object that you obtained somewhere else, maybe with complex configuration applied upon it, or your class does not expose a public, parameterless constructor. In these cases, you would need to register the instance that you have because, out of the box, Unity wouldn’t know how to build it.

Tip: Objects registered by RegisterInstance are singletons and those registered by RegisterType are created by default every time they are requested.

Note: Unity always registers itself automatically under the IUnityContainer key, with no name.

Note: Try to keep all of your registrations in the same place (bootstrap method) as this makes them easier to find and change.

Generic Types

But what about open (generic) types? Very easy:

public interface IGeneric<T>

{

    T Member { getset; }

}

 

public class Generic<T> : IGeneric<T>

{

    public T Member { getset; }

}

unity.RegisterType(typeof(IGeneric<>), typeof(Generic<>));

Unity lets you resolve an open type but you have to explicitly specify its generic parameter.

Creation Factories

A slightly more advanced registration option consists of using a delegate to build the class instance. We register a construction delegate as this:

unity.RegisterType<ILogger>("Factory"new InjectionFactory(

        u => new ConsoleLogger() { File = "out.log" } //or use your factory method

));

InjectionFactory is one kind of injection member, which will be covered in more detail in the Chapter 3  Dependency Injection chapter.

Base Types

Just for fun, we can also register base types—strings, integers, Booleans, etc.—as instances but not as types:

unity.RegisterInstance<string>("One""1");

unity.RegisterInstance<string>("Two""2");

unity.RegisterInstance<int>("One"1);

unity.RegisterInstance<int>("Two"2);

I will leave it to you to decide whether or not this is useful.

Registration by XML Configuration

XML Configuration Section

We just saw how to register mappings by code but it is certainly possible—and sometimes highly desirable—to do so by XML configuration, either through the App.config or the Web.config depending on your application type.

Note: If you use Visual Studio 2012, you can download the Enterprise Library Configuration Console from the Microsoft Enterprise Library Download Center. This helps you configure all Enterprise Library modules in the application configuration file through a user interface.

So, let’s declare a Unity configuration section in our configuration file and add a Unity entry:

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

<configuration>

    <configSections>            

        <section name="unity" type="Microsoft.Practices.Unity.Configuration.

UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>

    </configSections>

    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">     

        <container>     

            <register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.

ConsoleLogger, Succinctly" name="Console"/>              

            <register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.

FileLogger, Succinctly" name="File"/>    

        </container>

    </unity>

</configuration>

In a nutshell, inside the unity section you add a container element and, inside of it, you add a number of register declarations. A register section expects the following attributes:

  • type (required): The key to the registration, either a base type or an interface.
  • mapTo (required): The component (concrete class) type.
  • name (optional): An optional name to give to the registration; the default is null.

Tip: Do use full assembly qualified type names in your XML configuration so that Unity can find the types.

These are the exact same declarations as in the code example, with the obvious exception of instance registrations. There is no easy way to register an instance in XML since an instance is something that exists in memory.

Generic Types

If you want to register a generic type in XML:

<container>     

    <register type="Succinctly.IGeneric`1, Succinctly" 

        mapTo="Succinctly.Generic`1, Succinctly" name="Console"/>         

</container>

This uses the same syntax that .NET expects so make sure you use it properly.

Type Aliases and Assembly Discovery

You can create type aliases, which can be valuable for avoiding long type names. Here’s how it goes:

<unity> 

    <alias type="Succinctly.IGeneric`1, Succinctly" alias="GenericInterface"/>             

    <alias type="Succinctly.Generic`1, Succinctly" alias="GenericImplementation"/>         

</unity>

If you want to avoid specifying assembly qualified type names, you can add assembly names to a list that Unity uses for type discovery:

<unity> 

    <assembly name="Succinctly"/>         

</unity>

And the same goes for namespaces:

<unity> 

    <namespace name="Succinctly"/>               

</unity>

Using an alias simplifies the configuration:

<unity> 

    <register type="GenericInterface" mapTo="GenericImplementation" name="Console"/>

</unity>

IntelliSense

In order to make your life easier, you can add IntelliSense to the XML configuration. In Visual Studio, open up your configuration file and click on the ellipsis (…) next to Schemas in the Properties window:

XML schema configuration

Figure 5: XML schema configuration

You will be prompted with the schema configuration dialog. Click on Add, navigate to the packages\Unity.3.5.1404.0 folder below the solution folder, and select UnityConfiguration30.xsd:

XML schemas

Figure 6: XML schemas

This file contains the XML Schema Definition (XSD) for the Unity configuration elements. After following this step, you now have IntelliSense inside of the Unity section of the configuration file:

XML IntelliSense

Figure 7: XML IntelliSense

Loading XML Configuration

Before you can use XML configuration, you need to call the LoadConfiguration extension method from UnityContainerExtensions:

unity.LoadConfiguration();

Tip: Don’t forget to call LoadConfiguration() otherwise your XML registration will not be found.

Registration by Convention

While the previous techniques give us a lot of power, they do force us to register all entries manually, which can be a lot of work. Fortunately, Unity has the ability to register types automatically by applying conventions. This ability comes in the form of extension methods in the class UnityContainerRegistrationByConventionExtensions. Here is one example of a call to the RegisterTypes method:

unity.RegisterTypes

(

    AllClasses.FromLoadedAssemblies()

        .Where(x => (x.IsPublic) && (x.GetInterfaces().Any()) && (!x.IsAbstract) 

        && (x.IsClass)),

    WithMappings.FromAllInterfacesInSameAssembly, 

    type => (unity.Registrations

        .Select(x => x.RegisteredType)

        .Any(r => type.GetInterfaces()

        .Contains(r))) ? WithName.TypeName(type) : WithName.Default(type)

);

This needs a bit of explaining:

  • The first argument to RegisterTypes is the list of types to register. In this case, we are passing a list of public, non-interface, non-abstract classes from the currently loaded assemblies (AllClasses.FromLoadedAssemblies) that implement at least one interface. Classes can also be obtained from a list of assemblies (AllClasses.FromAssemblies) or from the file system (AllClasses.FromAssembliesInBasePath).
  • The second argument is a delegate that returns, for each registered type, its mapping key. In this case, all interfaces that the class exposes, declared in the same assembly as the class (WithMappings.FromAllInterfacesInSameAssembly). Another possibility is all of the type’s interfaces (WithMappings.FromAllInterfaces) or the type’s single interface that matches the type’s name (WithMappings.FromMatchingInterface).
  • The third parameter is how the registration shall be named. Here we are doing something clever: if there is already a registration for the same key, then use the full registered class name as the name (WithName.TypeName); otherwise, use null (WithName.Default). This example first creates a default, unnamed registration for the first interface and class found, and then all other classes are registered under their full name.
  • An optional fourth parameter, if present, must contain a delegate that determines the lifetime manager for each of the created instances. Common values are WithLifetime.ContainerControlled for singletons, WithLifetime.Transient for always creating new instances, and WithLifetime.PerThread for creating an instance per thread. More on this as well as a full list of options in the section Lifetime Managers.

Component Resolution

Resolving an Instance

Once we have our registrations, we can ask for an instance (resolve) of a component. In Unity, this is achieved through the Resolve method or one of the similar extension methods in UnityContainerExtensions. Some examples of the strongly typed (extension) versions are:

var logger1 = unity.Resolve<ILogger>();          //ConsoleLogger

var logger2 = unity.Resolve<ILogger>("File");    //FileLogger

var logger3 = unity.Resolve<ILogger>("Console"); //ConsoleLogger

And some examples of the dynamic (not strongly typed) ones:

var logger1 = unity.Resolve(typeof(ILogger)) as ILogger;                                  //a ConsoleLogger

var logger2 = unity.Resolve(typeof(ILogger), "File"as ILogger;

var logger3 = unity.Resolve(typeof(ILogger), "Console"as ILogger;

Tip: If the registration you are trying to obtain does not exist, Unity will throw an exception.

Resolving Multiple Instances

If we have multiple named registrations for the same type, we can retrieve them all at once:

var loggers1 = unity.ResolveAll<ILogger>();      //ConsoleLogger and FileLogger

var loggers2 = unity.ResolveAll(typeof(ILogger)) as IEnumerable<ILogger>;

Tip: Only named registrations are returned by ResolveAll.

Note: Unity resolves types regardless of the registration method.

The same unnamed ILogger instance, registered through RegisterInstance, will always be retrieved whenever Resolve is called without a name. This is because we registered an instance, not a type. For entries registered with RegisterType, new instances of the concrete classes are instantiated and returned every time. We discuss this behavior further in the following section Lifetime Managers.

Resolving with Overrides

It may happen that you want to resolve a component with different parameters (constructor or method arguments, property values) than the ones with which it was registered. I’m not saying that you should do this as this requires knowledge of how the component was registered which, in a way, defies the purpose of IoC. But, nevertheless, Unity allows you to do this.

The Resolve method takes an optional array of InjectionMember instances. Two of them (ParameterOverride and PropertyOverride) can be used to specify alternative values for constructor or method parameters or properties that were configured at registration time. For example, if you configured an InjectionProperty (see Property Injection for an explanation on this) to supply some value for some property, now you can change it to have a different value. This requires knowledge of how the component was registered because you can only supply alternative values for those members that were configured.

Here’s an example, with an overridden property value:

public class FileLogger : ILogger

{

    public string Filename { getset; }

}

//registration with injected property

unity.RegisterType<ILoggerFileLogger>(new InjectionProperty("Filename", "log.txt"));

//resolution with overridden property

unity.Resolve<ILogger>(new PropertyOverride("Filename", "output.log"));

And another one for constructor parameter overriding:

public class FileLogger : ILogger

{

    public FileLogger(string filename)

    {

        this.Filename = filename;

    }

                  

    public string Filename { getset; }

}

//registration with injected constructor

unity.RegisterType<ILoggerFileLogger>(new InjectionConstructor("log.txt"));

//resolution with overridden constructor parameter

unity.Resolve<ILogger>(new ParameterOverride("output.log"));

By default, overridden members get applied throughout all of the resolution hierarchy. For example, the class you asked for could have injected properties of other classes and some of these classes could have compatible injected members as well. But you can also cause the overriding to only occur in a specific type instead of all types that have compatible members, by means of OnType and OnType<T>:

//resolution with overridden property but only on instances of FileLogger

unity.Resolve<ILogger>(new PropertyOverride("Filename", "output.log") .OnType<FileLogger>());      //OnType(typeof(FileLogger))

Three things to keep in mind:

  • If using a lifetime manager that keeps existing instances, constructor overriding will not work since the object was already instantiated.
  • Overriding will only work for members configured at registration time.
  • Try to define the target for your member overriding by specifying OnType or OnType<T>.

Note: Try to avoid using resolution overrides as they are not a good idea.

Deferred Resolution

Now, it may happen that we wish to have a reference from a component at hand but may not need to use it immediately (if at all). If that is the case, we can obtain lazy loading references in the form of a Lazy<T> or Func<T>. This is called deferred resolution:

var lazyLogger = unity.Resolve<Lazy<ILogger>>();

var loggerDelegate = unity.Resolve<Func<ILogger>>();

The actual component is only instantiated when we access its value:

var logger1 = lazyLogger.Value;  //lazyLogger.IsValueCreated = true

var logger2 = loggerDelegate();

Tip: You don’t have to register a type with Lazy<T> or Func<T>, Unity does it for you.

Testing If a Registration Exists

If you want to test whether or not a given registration exists, you have two options. You can either look at the Registrations property or use one of the IsRegistered extension methods in UnityContainerExtensions. Both of these methods look internally at the Registrations collection. We can also do so explicitly, as in this example where we retrieve all registered ILogger concrete types and their registration names:

var loggerTypes = unity

        .Registrations

        .Where(x => x.RegisteredType == typeof(ILogger))

        .ToDictionary(x => x.Name ?? String.Empty, x => x.MappedToType);

Because the resolve methods throw exceptions if the registration is not found, it is sometimes useful to use a helper method such as this:

public static T TryResolve<T>(this IUnityContainer unity, String name = null)

{

    return ((T) (TryResolve(unity, typeof(T), name) ?? default(T)));

}

public static Object TryResolve(this IUnityContainer unity, Type type, 

String name = null)

{

    try

    {

        return (unity.Resolve(type, name));

    }

    catch

    {

        return (null);

    }

}

Lifetime Managers               

We have seen that RegisterInstance and RegisterType behave differently. By default, RegisterInstance always causes the same registered instance to be returned in all subsequent calls to Resolve while RegisterType does exactly the opposite as a new instance is always returned. This behavior comes from the default lifetime manager used by each method. If you look at the documentation for each of the Register* methods, you will see that they take an optional LifetimeManager instance, the base class for all lifetime managers.

A lifetime manager handles how an instance of a registered type is created and all registrations have an associated lifetime manager or null. A null lifetime manager will always cause a new instance to be created every time the Resolve method is called. Unity includes a number of lifetime managers, described in the following table:

Table 1: Lifetime managers

Lifetime Manager

Alias (XML/conventional configuration)

Purpose

ContainerControlledLifetimeManager

singleton/ WithLifetime.ContainerControlled

Unity creates a single instance of the type (a singleton) and always returns it. When the container is disposed, if the registered type implements IDisposable, Unity also disposes of it.

ExternallyControlledLifetimeManager

external/ WithLifetime.ExternallyControlled

Registers an instance created elsewhere (not by Unity). Does not dispose of the registered instances. WithLifetime.ExternallyControlled, in conventional configuration.

HierarchicalLifetimeManager

hierarchical/ WithLifetime.Hierarchical

Identical to ContainerControlledLifetimeManager but, in child containers, each gets its own instance. Disposes of created instances when the container is disposed.

PerRequestLifetimeManager

N/A

In web applications, Unity looks for an instance of the type in the HttpContext.Items collection and, if not found, creates a new instance and stores it there. Only disposes of created instances if module UnityPerRequestHttpModule is enabled (discussed in Introduction

A number of technologies in the Microsoft stack use IoC. In fact, its number seems to be growing every time a new version or technology is launched. It is generally possible to plug in an IoC container of choice; in our case, let’s see how to use Unity.

ASP.NET Web Forms). Requires NuGet package Unity.Mvc.

PerResolveLifetimeManager

perresolve/ WithLifetime.PerResolve

During a resolve operation, if an instance of a type is needed multiple times, only creates one instance and reuses it throughout the operation. In different resolves, always creates new instances. Disposes of created instances when the container is disposed.

PerThreadLifetimeManager

perthread/ WithLifetime.PerThread

Creates a new instance per thread and always returns that same instance to the same thread. Disposes of created instances when the container is disposed.

TransientLifetimeManager

transient/ WithLifetime.Transient

Always creates and returns a new instance of the registered type. Disposes of created instances when the container is disposed. Identical to passing null. The default for RegisterType.

Lifetime manager class hierarchy

Figure 8: Lifetime manager class hierarchy

Note: Singleton and transient are type aliases. See Type Aliases.

So, we have three kinds of lifetime managers:

Tip: The only meaningful lifetime managers that can be passed to RegisterInstance are ContainerControlledLifetimeManager (the default) or ExternallyControlledLifetimeManager. The difference between the two is that the latter does not dispose of the registered instance when the container is disposed.

Tip: To use PerRequestLifetimeManager, you will need to add a reference to the Unity.Mvc NuGet package.

Remember the Singleton design pattern? Well, in the age of IoC containers, you might as well consider it an anti-pattern. You no longer have to implement your classes in a funny way whenever you want to have a singleton, nor do you have to watch out for all possible alternative ways developers can use to fool your code. Just configure a component to have a lifetime of singleton and ask Unity for it. That’s it. If you want to change this behavior later on, it is just a matter of configuring it otherwise.

An example of registering a different instance of a type per calling thread, in configuration by code, would be:

unity.RegisterType<ILoggerConsoleLogger>(new PerThreadLifetimeManager());

And in XML configuration, notice how we can use either the lifetime manager’s alias or its full class name:

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

<configuration>

    <configSections>            

        <section name="unity" type="Microsoft.Practices.Unity.Configuration.

UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>

    </configSections>

    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity">     

        <container>     

            <register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.

ConsoleLogger, Succinctly" name="Console">       

                <lifetime 

type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,  Microsoft.Practices.Unity"/>

            </register> 

            <register type="Succinctly.ILogger, Succinctly" mapTo="Succinctly.

FileLogger, Succinctly" name="File">

                <lifetime type="transient"/>

            </register>

        </container>

    </unity>

</configuration>

Tip: If you are not using an alias, make sure you use the assembly qualified name of the lifetime manager class.

Finally, in conventional configuration, we can also specify the lifetime manager for each registration. For that, we need to pass a delegate as the fourth parameter to RegisterTypes. This delegate receives a type as its only argument and returns a LifetimeManager instance. The class WithLifetime has some predefined methods for the most usual lifetime managers and there’s also Custom<T>, which can be used to return a custom one (PooledLifetimeManager is defined in chapter Chapter 5  Extending Unity):

unity.RegisterTypes

(

    AllClasses.FromLoadedAssemblies()

        .Where(x => (x.IsPublic == true) && (x.GetInterfaces().Any() == true

        && (x.IsAbstract == false) && (x.IsClass == true)),

    WithMappings.FromAllInterfacesInSameAssembly, 

    type => (unity.Registrations.Select(x => x.RegisteredType)

        .Any(r => type.GetInterfaces().Contains(r) == true) == true) ? 

    WithName.TypeName(type) : WithName.Default(type),

    WithLifetime.Custom<PooledLifetimeManager>()

);

Tip: Lifetime manager instances cannot be shared by different registrations and the Register* methods will throw an exception if you try.

Child Containers

Creating a child container can help in managing instances in a broader scope than that of the Unity container. A child container has these three characteristics:

  • All registrations from the parent are accessible in a child container.
  • Registrations originating in the child container are not accessible in its parent.
  • Disposable instances created by a child container are disposed of when the child container is itself disposed.

We create a child container by calling CreateChildContainer:

using (var child = unity.CreateChildContainer())

{

    child.RegisterType<ILoggerConsoleLogger>("DisposableChild"

        new ContainerControlledLifetimeManager());

    var logger = child.Resolve<ILogger>("DisposableChild");

}       //logger.Dispose is called

Tip: Don’t forget that Dispose is only called for some lifetime managers.

Since IUnityContainer implements IDisposable, it is a good principle to always create child containers in a using block so that they are properly disposed of when no longer needed.

A child container exposes the same interface as the parent—IUnityContainer—which has a Parent property but does not keep track of its children.

A typical scenario in which you would use a child container would be in a web application request. We’ll look at that when we talk later about Unity’s integration with Introduction

A number of technologies in the Microsoft stack use IoC. In fact, its number seems to be growing every time a new version or technology is launched. It is generally possible to plug in an IoC container of choice; in our case, let’s see how to use Unity.

ASP.NET Web Forms.

Common Service Locator

A number of IoC containers exist in the .NET world including Unity, Autofac, Ninject, Spring.NET, StructureMap, and Castle Windsor. Although each one has specific functionality, when it comes to resolving a registered component, they all have a similar interface consisting of two basic operations:

  • “Get me an instance of type XYZ identified by key ABC”.
  • “Get me all instances of type XYZ”.

Having that in mind, the development community, together with Microsoft, defined and implemented a common interface to which all IoC containers can comply called the Common Service Locator.

The Common Service Locator is an implementation of the Service Locator pattern maintained by Microsoft and hosted here at CodePlex. If you added Unity as a NuGet package, you might have noticed that the Common Service Locator package came along as well.

It serves as a central repository that abstracts whatever IoC container you want to use. With the Common Service Locator, you don’t have to expose Unity as a shared field.

The Common Service Locator developers have implemented adapters for some of the most popular IoC containers, all of which are available at the Common Service Locator site. Other vendors have implemented adapters for their own products. In the case of Unity, all it takes to set it up is:

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(unity));

After that, instead of directly referencing Unity, you can instead reference the Common Service Locator.

So, where you had:

var logger = unity.Resolve<ILogger>();

You can now have:

var logger = ServiceLocator.Current.GetInstance<ILogger>();

And that’s it. This way, you can even replace Unity with another IoC container and your code won’t even notice it.

For the record, the interface exposed by the Common Service Locator is IServiceLocator:

public interface IServiceLocator : IServiceProvider

{

    IEnumerable<TService> GetAllInstances<TService>();

    IEnumerable<object> GetAllInstances(Type serviceType);

    TService GetInstance<TService>();

    TService GetInstance<TService>(string key);

    object GetInstance(Type serviceType);

    object GetInstance(Type serviceType, string key);

}

As you can see, only component resolution methods are supported, not registration. There are strongly typed generic methods as well as dynamic methods that return plain objects. Calls to these methods will be directed to the proper implementation; in our case, the Unity container instance. So anything that applies to Unity also applies to the Common Service Locator, such as an exception being thrown in case a registration is not found.

Note: If you ever want to go from the Common Service Locator back to Unity, you just have to retrieve the unnamed IUnityContainer instance.

One final note: The IServiceLocator inherits from IServiceProvider, an interface used since the early days of .NET to offer a kind of poor man’s IoC. It is still used by the Visual Studio designer and by some APIs such as Runtime Caching and WCF Data Services.

Note: Some people think that the Service Locator pattern is actually a bad thing. I understand the arguments but don’t quite agree with them. For in-depth coverage on this, read the blog post “Service Locator is an Anti-Pattern” by Mark Seemann.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.