Rabehaja Loïc

View Original

Sitecore's Dependency Injection container... Hot or not?

Sitecore 9 + in-built Dependency Injection (DI) container + Helix, Sitecore's game is literally on point! Let's take this opportunity to take a look at some intrisic steps that Sitecore made a year ago. Since Sitecore's redesign of its framework, (from Sitecore 8.2) it comes with a few architectural changes that makes it more modular and support their modular/layer based architecture concept, Helix. One of them is the integration of their own DI container.

After reading this article, you will have a clear understanding on how Sitecore's DI container works. At the end of this article, you will find a link to another post explaining how to modify it accordingly (while still remaining within Sitecore's boundaries) if the Out-of-The-Box DI does not fit your needs.


Table of contents


Why use the built-in container?


This must be the question that comes through the mind of most of the people out there. Since there are a lot of DI container that works well, why should I use Sitecore's DI container? If you are comfortable using one of those full featured frameworks and you are using a pre Sitecore 8.2 version, there is really no reason for you to make the switch, however, if your project does not use one yet, you only make a really light use of one, or you are just running on a 8.2 or up version of Sitecore, you might really consider making the switch in order to reduce overhead of using a large DI framework and also to stay within Sitecore's standard by using the built-in DI container.


The basics


If you are already familiar with microsoft dependency injection, getting used to Sitecore's is not a big change. The instantiation of the builder instead of being instantiated in an App_Start.cs or a global.asax.cs file, it is done through a pipeline.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <serviceProviderBuilder type="Sitecore.DependencyInjection.DefaultServiceProviderBuilder, Sitecore.Kernel" />
  </sitecore>
</configuration>

And the services are being registered in the service collection using specific service configurator pipeline

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <services>
      <configurator type= "Vanilla.DependencyInjection.DI.Pipelines.Feature.ServiceConfigurator, Vanilla.DependencyInjection.Unity"/>
    </services>
  </sitecore>
</configuration>

That is how to use and to configure Sitecore DI container. If you are wishing to know more about how to set it up you can go through @kamsar's article or Sitecore's documentation. From now on, we will move a bit in depth on how it works.


The dive


Now that I know how to use it, I would like to know how does Sitecore builds its ServiceProvider!
Sitecore uses 3 layers of abstractions based on the Microsoft.Extensions.DependencyInjection which are described below.

Sitecore Dependency Class Diagram

The DefaultServiceProviderBuilder inherits from the class DefaultBaseProviderBuilder which implements the abstract class BaseserviceProviderBuilder which inherits from BaseServicesConfiguratorFactory which implements IServiceConfigurator (More explict information below).


IServiceConfigurator Dependency Diagram


IServiceConfigurator plays a key role here. This is the interface is being implemented wherever Sitecore needs a Service to be registered into its DI container as the

namespace Sitecore.DependencyInjection
{
  /// <summary>The default service provider builder.</summary>
  public class DefaultServiceProviderBuilder : DefaultBaseServiceProviderBuilder
  {
    /// <summary>
    /// Adds ServiceScopeConfigurator to default configurators.
    /// </summary>
    /// <returns>The collection of configurators.</returns>
    public override IEnumerable<IServicesConfigurator> GetServicesConfigurators()
    {
      foreach (IServicesConfigurator servicesConfigurator in base.GetServicesConfigurators())
        yield return servicesConfigurator;
      yield return (IServicesConfigurator) new ServicesScopeConfigurator();
    }
  }
}


namespace Sitecore.DependencyInjection
{
  /// <summary>The service provider configurator.</summary>
  public class DefaultBaseServiceProviderBuilder : BaseServiceProviderBuilder
  {
    /// <summary>Build service provider form a collection.</summary>
    /// <param name="serviceCollection">The service collection.</param>
    /// <returns>The <see cref="T:System.IServiceProvider" />.</returns>
    protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
    {
      return serviceCollection.BuildServiceProvider();
    }
  }
}


namespace Sitecore.DependencyInjection
{
  /// <summary>The service provider configurator interface.</summary>
  public abstract class BaseServiceProviderBuilder : BaseServicesConfiguratorFactory
  {
    /// <summary>The configure.</summary>
    /// <returns>The <see cref="T:System.IServiceProvider" />.</returns>
    public IServiceProvider Build()
    {
      return this.BuildServiceProvider(this.ConfigureServiceCollection());
    }
  }
}


The flow of Sitecore's DI container

  1. When your Sitecore instance is being triggered, the magic begins, the Build() method from the BaseServiceProviderBuilder is called.
  2. It will call public override IEnumerable<IServicesConfigurator> GetServicesConfigurators() from Sitecore.DependencyInjection.DefaultServiceProviderBuilder to build the default configurators including the scope configurators.
    • From this part on, every Sitecore's default Services will be registered. (It is important to note that default services cannot be seeing in the Sitecore configuration as they are registered directly in the code).
  3. Then finally call BuildServiceProvider(IServiceCollection serviceCollection) from Sitecore.DependencyInjection.DefaultBaseServiceProviderBuilder.


Performance


As we've seen earlier, Sitecore's DI purely use Microsoft.Extensions.DependencyInjection to its fullest. There is not custom implementation or whatsoever that makes it derive from it.

If you've reach this part of the article, that would mean that, like me, you care about the performance of your application. Or you might have the "I am considering of using it but how good it will perform?".
I will compare it to some of the most popular DI framework currently available today. For this test case, I've chosen Unity, SimpleInjector and Ninject. The list is quite long but for the purpose of this article, those would be enough to demonstrate our case.

The answer to that is quite simple and straightforward, it is performing! Let me re-phrase it. For the sole purpose of being a DI container, i.e. registring and resolving your services, it does perform very well. If you want see a full report of the performance of this specific container you can read this article


Conclusion: Sitecore's Dependency Injection is HOT!


The Sitecore implementation of DI is based on the Microsoft.Extensions.DependencyInjection abstractions from ASP.NET Core. This built-in DI container is not intended to replace more full-featured DI frameworks such as StructureMap, Unity, SimpleInjection, Ninject, Autofac, and others) but rather provides a good basis and a common DI Abstractions which allows you to replace the built-in container, with any other DI Framework. There may be various discussion on whether to use it or not, but in one word, Sitecore strikes hard by encouraging good practice.

Whether you choose to use the built in container or a third party container will depend on whether the built-in container is powerful and complete enough for your given project. For small projects, it should runs perfectly fine, but if you need convention based registration, logging/debugging tools or more advance features like property injection they are pretty easy to integrate. For example you can take a look at @guitarrich with his implementation or the one I made replacing the provider with Unity.