Replacing the Default DI Container in Sitecore 8.2

With the release of 8.2, out-of-the-box Sitecore now uses IServiceProvider — ASP.NET’s own dependency injection abstraction, as well as its default implementation. If you haven’t yet, go read Kam’s article “Dependency Injection in Sitecore 8.2” that describes how to use the built-in capabilities of the new Sitecore DI.

Since Sitecore has become DI-aware, its internals have undergone significant rewiring, allowing for a more seamless implementation swapping — not only for its internal components, but even for some parts of its infrastructure.

Why replace the default container?

So you already have a dependency injection container coming with Sitecore. Why would you want to substitute it with something else? Well, your reasons could be many:

  • IServiceProvider is a conforming container, which is considered an anti-pattern. As a matter of fact, there was a very thought-provoking discussion thread involving ASP.NET Core developers from one side and dependency injection library authors from the other side, going back and forth on whether or not the IServiceProvider abstraction should be removed altogether (spoiler alert: it wasn’t removed).
  • You may already be using another container and don’t want to migrate to IServiceProvider.
  • The default implementation of IServiceProvider may not have enough features for your needs. Being a conforming container, its capabilities are the lowest common denominator of the modern DI containers, and so things like SimpleInjector’s diagnostic and verification abilities or Autofac’s resolve-time parameters are unavailable with the default container. Even such a simple thing as automatic registration of all MVC controllers will require 90 lines of custom code.

I do believe that using one of the leading DI libraries will provide an overall better development experience, compared to using the default container.

Replacing the DI implementation via <initialize>

If you wanted to use a Dependency Injection container with Sitecore 8.1, there was, essentially, one best way of doing that:

  1. Create an <initialize> pipeline processor and use it to configure your custom DI container;
  2. Replace the dependency resolvers of ASP.NET MVC and WebAPI with dependency resolvers provided by your DI container.

You could additionally use tricks like initializing each module’s services separately via custom pipeline and falling back to Sitecore’s dependency resolver for improved results.

Of course, you can still use a similar approach in 8.2, and there’s nothing wrong with that. But given Sitecore’s new DI-related features, you should consider using the new and simpler approach.

Replacing the DI implementation via <serviceProviderBuilder>

Disclaimer: Most modern DI containers provide IServiceProvider adapters; I will use Autofac in this example. You’ll need to install the following NuGet packages: Autofac, Autofac.Mvc5, and Autofac.Extensions.DependencyInjection.

If you look at the Sitecore.config included with 8.2, you’ll see a new configuration node added at the very bottom:

<serviceProviderBuilder type="Sitecore.DependencyInjection.DefaultServiceProviderBuilder, Sitecore.Kernel" />

This node references the class that will return an implementation of IServiceProvider. That service provider instance will then be used everywhere throughout the application, including Sitecore’s internal components.

Let’s make our own service provider builder using the class below.

public class AutofacServiceProviderBuilder
: BaseServiceProviderBuilder
{
protected override IServiceProvider BuildServiceProvider(IServiceCollection serviceCollection)
{
var builder = new ContainerBuilder();
    // Register Sitecore services in Autofac.
builder.Populate(serviceCollection);
    // Register our custom services via a module.
builder.RegisterModule<ServicesModule>();
    // Register all MVC controllers in the current assembly.
builder.RegisterControllers(Assembly.GetExecutingAssembly());
    IContainer container = builder.Build();
    // Return Autofac's service provider adaptor.
return container.Resolve<IServiceProvider>();
}
}

Inheriting from Sitecore.DependencyInjection.BaseServiceProviderBuilder ensures that Sitecore's internal service configurators are utilized.

Notice the use of builder.Populate(serviceCollection)—this is an extension method included in Autofac.Extensions.DependencyInjection. It goes through all existing services (registered so far by Sitecore) and registers them in Autofac, which makes them available in your DI container. If Sitecore registers more services later on, they will be registered in Autofac through the IServiceProvider adaptor we returned from BuildServiceProvider().

Now that we have our own builder class, let’s replace the DefaultServiceProviderBuilder. To do that, create a configuration patch file with the following contents and put it under /App_Config/Include.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<serviceProviderBuilder>
<patch:attribute
name="type"
value="[Namespace].AutofacServiceProviderBuilder, [Assembly]" />
</serviceProviderBuilder>
</sitecore>
</configuration>

And this is it! No need for custom controller factories, or chained dependency resolvers.

An attentive reader may have already noticed that we din’t even have to touch DependencyResolver.Current. This is because Sitecore's MVC dependency resolver will internally use IServiceProvider—the one we provided when we overrode the <serviceProviderBuilder>.

By the way, you can see all service registrations on a new administrative page: /sitecore/admin/ShowServicesConfig.aspx. It will list all available services — registered either by you and by Sitecore.

Registering services in .config files

There’s a bonus: since we’ve completely replaced the default container, we can now use Sitecore XML configuration to register services in our DI container:

<sitecore>
<services>
<register
serviceType="[Namespace].ISomeService, [Assembly]"
implementationType="[Namespace].ISomeImplementation, [Assembly]"
lifetime="Transient" />
</services>
</sitecore>

This syntax has even fewer features than IServiceProvider’s API, but still, XML configuration may work for simple cases — if that’s something you’re into.

Conclusion

The approach defined above is very clean and straightforward. The way in which Sitecore allows to swap its internal dependency resolution is definitely a big step forward. Your custom services, along with Sitecore’s internal services, are now easily accessible, replaceable and injectable through your DI container of choice. All with minimal code and configuration.