Boost your Helix by overriding the default ServiceProvider with Unity!

Couple of weeks ago, Sitecore released its 9th version of its product. For those you haven't already checked it out, I urge you to do so. There are a whole set of features that make development very enjoyable with an out of the box flavour.
Among those features, the already famous Sitecore Dependency Injection (DI) support out of the box since Sitecore 8.2 with its Inversion of Control (IoC).

What if this default container does not fit your need for whatever reason but you still want to override the container the way Sitecore described it in its documentation?

This article provides you with a more in-depth "how-to" steps with detailed explanations and includes the link to my source code as well at the end of the article.


In this article


What is expected from this article?


After reading this article, you will be able to:

  • Override Sitecore's Default DI container with another one of your liking
  • No new pipelines added to make it work
  • Configure your new DI container using Sitecore's Service Configurators.


Why choose a different container?


Sitecore have designed their framework to use dependency injection as standard everywhere as from Sitecore 8.2. It is based on Microsoft.Extensions.DependencyInjection which is pretty much performant and reliable and already used by Sitecore itself. If you are curious about the performance comparison between Sitecore's in-built container and other popular containers, you can have a look at this article.


If everything works already fine, why should I choose another one? Well, since Sitecore uses Microsoft's dependency injection, it is a basic container that allows you to use DI in its most simplistic way (register and consume). If your project requires more advanced features such as property injection or convention based registration, you might have to consider using a more fully featured IoC container.

There are many possibilities(e.g Autofac, Ninject) but amongst the list I randomnly picked Unity, so it will be the container we will be working on today.


"Aan de slag" - Let's put it to work!


The first and most important of all step is to ready Sitecore's documentation about its DI Container. It gives a pretty clear guideline on how to work and override it with your own.

You can replace the service provider. Sitecore uses the Microsoft.Extensions.DependencyInjection package default.

To replace default provider:

Inherit from Sitecore.DependencyInjection.BaseServiceProviderBuilder and implement the BuildServiceProvider method:

public class MyProviderBuilder : BaseServiceProviderBuilder
{
    protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
    {
        return MyFavouriteServiceProvider.Build(serviceCollection);
    }
}

Replace the default builder in Sitecore.config with your builder: <serviceProviderBuilder type="Sitecore.DependencyInjection.DefaultServiceProviderBuilder, Sitecore.Kernel"/> You can also patch this node with an include file: <serviceProviderBuilder type="MyProviderBuilder"/>

1. Create our custom ProviderBuilder:


Let's start by creating a scaffolding of our soon to be UnityServiceProvider. For now, I will make it return null because at this point the engine is not ready yet.


namespace Vanilla.DependencyInjection.DI.Provider
{
    public class UnityServiceProviderBuilder : BaseServiceProviderBuilder
    {

        protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
        {
           return null;
        }

    }
}


2. Create our UnityServiceProvider.cs class:


To be able to use Unity with Sitecore (following Sitecore's guideline), I need a custom IServiceProvider to act as a wrapper for my IUnityContainer. (ASP Documentation)

namespace Vanilla.DependencyInjection.DI.Unity
{
    public class UnityServiceProvider : IServiceProvider
    {
        private readonly UnityContainer _container;

        public UnityContainer Container => _container;

        public UnityServiceProvider()
        {
            _container = new UnityContainer();

        }

        public object GetService(Type serviceType)
        {
            ///Let the container resolve the service
            return _container.Resolve(serviceType);
        }
    }
}


At this point, I have my custom IServiceProvider which will be used instead of the default one. It will be used at the later stage to wire up our Unity stuffs.


3. Resolving our controller


MVC uses an IControllerActivator interface to handle all the Controller instantiation. What does it means? By creating a custom controller activator, I do not need to load my current assemblies and load every controller to register them anymore.


namespace Vanilla.DependencyInjection.DI.Unity
{
    public class UnityControllerActivator : IControllerActivator
    {
        private IUnityContainer _unityContainer;

        public UnityControllerActivator(IUnityContainer container)
        {
            _unityContainer = container;
        }

        #region Implementation of IControllerActivator

        public void Release(ControllerContext context, object controller)
        {
            //ignored
        }

        #endregion

        public IController Create(RequestContext requestContext, Type controllerType)
        {
            return _unityContainer.Resolve(controllerType) as IController;
        }
    }
}


Important note: You can also use Kamsar's approach here by directly creating an extension to the ServiceCollection class and calling an extension method of the ServiceCollection to register all controllers from our ServiceConfigurator class.
But since I choose unity, I decided to go all the way up with the Unity!


4. Provide a fallback method for Sitecore's registered services.

At this point, we pretty much have it, but I would have a problem. If I use only my UnityServiceProvider, my application will not be able to start. The simple reason is that, Sitecore uses its default service provider to register and resolve its internal services. I have two options, either, I re-register everything already register into unity again, or I can create some sets of fallback methods to tell my custom service provider to try to resolve first using the Unity container and if not being able to resolve them, fallback to the default service provider to resolve them using the UnityContainerExtension.


- UnityFallBackProviderExtension


namespace Vanilla.DependencyInjection.DI.Unity
{
    public class UnityFallbackProviderExtension : UnityContainerExtension
    {
        #region Const

        ///Used for Resolving the Default Container inside the UnityFallbackProviderStrategy class
        public const string FALLBACK_PROVIDER_NAME = "UnityFallbackProvider";

        #endregion

        #region Vars

        // The default Service Provider so I can Register it to the IUnityContainer
        private IServiceProvider _defaultServiceProvider;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of the UnityFallbackProviderExtension class
        /// </summary>
        /// <param name="defaultServiceProvider">The default Provider used to fall back to</param>
        public UnityFallbackProviderExtension(IServiceProvider defaultServiceProvider)
        {
            _defaultServiceProvider = defaultServiceProvider;
        }

        #endregion

        #region Overrides of UnityContainerExtension

        /// <summary>
        /// Initializes the container with this extension's functionality.
        /// </summary>
        /// <remarks>
        /// When overridden in a derived class, this method will modify the given
        /// <see cref="T:Microsoft.Practices.Unity.ExtensionContext" /> by adding strategies, policies, etc. to
        /// install it's functions into the container.</remarks>
        protected override void Initialize()
        {
            // Register the default IServiceProvider with a name.
            // Now the UnityFallbackProviderStrategy can Resolve the default Provider if needed
            Context.Container.RegisterInstance(FALLBACK_PROVIDER_NAME, _defaultServiceProvider);

            // Create the UnityFallbackProviderStrategy with our UnityContainer
            var strategy = new UnityFallbackProviderStrategy(Context.Container);

            // Adding the UnityFallbackProviderStrategy to be executed with the PreCreation LifeCycleHook
            // PreCreation because if it isnt registerd with the IUnityContainer there will be an Exception
            // Now if the IUnityContainer "magically" gets a Instance of a Type it will accept it and move on
            Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
        }

        #endregion
    }
}


- UnityFallbackProviderStrategy

namespace Vanilla.DependencyInjection.DI.Unity
{
    public class UnityFallbackProviderStrategy : BuilderStrategy
    {
        private IUnityContainer _container;

        public UnityFallbackProviderStrategy(IUnityContainer container)
        {
            _container = container;
        }

        #region Overrides of BuilderStrategy

        /// <summary>
        /// Called during the chain of responsibility for a build operation. The
        /// PreBuildUp method is called when the chain is being executed in the
        /// forward direction.
        /// </summary>
        /// <param name="context">Context of the build operation.</param>
        public override void PreBuildUp(IBuilderContext context)
        {
            NamedTypeBuildKey key = context.OriginalBuildKey;

            // Checking if the Type we are resolving is registered with the Container
            if (!_container.IsRegistered(key.Type))
            {
                // If not we first get our default IServiceProvider and then try to resolve the type with it
                // Then we save the Type in the Existing Property of IBuilderContext to tell Unity
                // that it doesnt need to resolve the Type
                context.Existing = _container
                    .Resolve<IServiceProvider>(UnityFallbackProviderExtension.FALLBACK_PROVIDER_NAME)
                    .GetService(key.Type);
            }

            // Otherwise we do the default stuff
            base.PreBuildUp(context);
        }

        #endregion
    }
}

We are almost there! With this implementation, I can "mix" dependencies. If I need any of my services AND an Ioptions interface from Sitecore, my unity container will resolve all of those and inject them into my controllers.


Important note: if the same interface is registered in both Unity and Sitecore's default DI Container, it will not try to resolve the service in the default container anymore as the unity container can already resolve it. It is then really important here to keep track of where you use the service.


5. Let's wire it up together!


namespace Vanilla.DependencyInjection.DI.Provider
{
    public class UnityServiceProviderBuilder : BaseServiceProviderBuilder
    {

        protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
        {
            var unityServiceProvider = new UnityServiceProvider();

            IUnityContainer container = unityServiceProvider.Container;
            // Adding the Controller Activator
            // Caution!!! Do this before you Build the ServiceProvider !!!
            serviceCollection.AddSingleton(container);
            serviceCollection.AddSingleton<IControllerActivator>(new UnityControllerActivator(container));
            //Now build the Service Provider

            var defaultProvider = serviceCollection.BuildServiceProvider();
            // Configure UnityContainer
            // #region Unity
            //Add the Fallback extension with the default provider
            container.AddExtension(new UnityFallbackProviderExtension(defaultProvider));
             //Set the resolver to unity.
            DependencyResolver.SetResolver(new UnityServiceLocator(container));
            return unityServiceProvider;
        }
    }
}


Important note: If I start my application at this moment, Sitecore will fail (See next point for the explanation).


6. Final touch.


If you fire up your Sitecore instance after this, it will throw an issue not being able to resolve some of Sitecore's default. The reason is that the BaseServiceProviderBuilder class is missing a method that Sitecore uses to register these dependencies.


public override IEnumerable<IServicesConfigurator> GetServicesConfigurators()
    {
      foreach (IServicesConfigurator servicesConfigurator in base.GetServicesConfigurators())
        yield return servicesConfigurator;
      yield return (IServicesConfigurator) new ServicesScopeConfigurator();
    }


I have two choices here: either, I copy this code an put in into my UnityServiceProvider or just simply make the class inherit from DefaultServiceProviderBuilder instead of BaseServiceProviderBuilder in order to avoid code redunduncy.

The final piece of code will look like:

namespace Vanilla.DependencyInjection.DI.Provider
{
    public class UnityServiceProviderBuilder : DefaultServiceProviderBuilder
    {

        protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
        {
            var unityServiceProvider = new UnityServiceProvider();

            IUnityContainer container = unityServiceProvider.Container;
            // Adding the Controller Activator
            // Caution!!! Do this before you Build the ServiceProvider !!!
            serviceCollection.AddSingleton(container);
            serviceCollection.AddSingleton<IControllerActivator>(new UnityControllerActivator(container));
            //Now build the Service Provider

            var defaultProvider = serviceCollection.BuildServiceProvider();
            // Configure UnityContainer
            // #region Unity
            //Add the Fallback extension with the default provider
            container.AddExtension(new UnityFallbackProviderExtension(defaultProvider));
             //Set the resolver to unity.
            DependencyResolver.SetResolver(new UnityServiceLocator(container));
            return unityServiceProvider;
        }
    }
}


Verification


I've made quite some changes here, let's make sure that my overrides are how they are supposed to be, smoothly integrated into Sitecore.

In order to verify if everything is smooth, let's check the class diagrams

IServiceLocator with unity

Voilà I'm all set! Ready to "Sitecore-rock" with Unity instead of Microsoft.Extensions.DependencyInjection.


Conclusion


As you can see, overriding Sitecore's default DI is not really difficult. This article mainly serves as example but you would have to follow the same principle as stated above to override the DI container with the one of your liking.

I've also put the code base of this implementation on my github. You will find there more explanation on how to use it.