Dependency Injection Conventions with ASP.NET Core 1.1 and Autofac

With the 1.1 release of ASP.NET Core, it has now become a little easier to configure 3rd party dependency injection containers into your application. Autofac.Extensions.DependencyInjection 4.1.0 was recently released with support for this new feature, so I figured I'd try it out.

DI Conventions

Ever since ASP.NET MVC was introduced, we've been accustomed to relying on conventions that make the development experience with the framework a little nicer. In ASP.NET Core, probably one of the best examples of this is the Startup class. This is a simple .NET class that we use to compose the various features of our application. A fairly barebones app only requires that you to create a Configure method that accepts an instance of IApplicationBuilder. Then by convention, the framework will discover this method and configure your application. For richer applications that require additional services and wish to leverage the built in dependency injection features, a ConfigureServices can be added to the Startup class which will accept an instance of IServiceCollection that you can use to wire up your services.

  public void ConfigureServices(IServiceCollection services)
  {
    services. AddTransient<IDataService, MyDataService>();
  }

Some of us might have existing investments in a particular DI framework that we'd like to integrate into the ASP.NET Core DI infrastructure. To do that, we'd need an implementation of IServiceProvider that would adapt your framework of choice. For example, if you use Autofac then the Configure method might look something like this.

  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc();

    // Create the container builder.
    var builder = new ContainerBuilder();   
    builder.RegisterType<MyDataService>().As<IDataService>();
    builder.Populate(services);
    var container = builder.Build();

    // Create the IServiceProvider based on the container.
    return new AutofacServiceProvider(container);
  }

Just like the Configure method, the framework will discover the implementation of ConfigureServices in the Startup class and run it for you.

Introducing ConfigureContainer

Starting with ASP.NET Core 1.1, 3rd party DI frameworks are able plug in earlier in the pipeline which has the added benefit of reducing the amount of code needed to use them. At the core of this is the IServiceProviderFactory interface. Frameworks like Autofac can implement this interface (see here). Then it can be registered when WebHostBuilder is getting setup. ASP.NET Core can now use this factory to replace its default service provider that gets created at startup. Let's see how we can update the example above to make use of this new feature.

First, make sure you have at least version 4.1.0 of Autofac.Extensions.DependencyInjection installed in your application. Next, we'll remove that Autofac setup code from ConfigureServices and add a new method to the Startup class, ConfigureContainer.

public void ConfigureContainer(ContainerBuilder builder) {
    builder.RegisterType<FakeDataService>().As<IDataService>();
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

Just like Configure and ConfigureServices, ConfigureContainer will get discovered and executed by convention when your application starts. You can also create variations based on the the hosting environment. So for example, you can have ConfigureDevelopmentContainer, ConfigureStagingContainer and so on.

The parameter to the ConfigureContainer method will be the type that you use to compose your DI container. In the case of Autofac, this happens with the ContainerBuilder class. Now any of the registration code for your 3rd party DI framework can now be placed inside of the ConfigureContainer method.

The last thing that needs to happen is for us to register the IServiceProviderFactory implementation. Autofac provides the AddAutofac extension method that helps out with that. Using the ConfigureServices method off of IWebHostBuilder, we can update the hosting bootstrap code to plug in Autofac.

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .ConfigureServices(services => services.AddAutofac())
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();
    host.Run();
}

Now a new IServiceProviderFactory is configured in the application and we can continue registering additional services with Autofac.

One mistake I made when I was first trying this out was I tried to call the AddAutofac extension method inside the ConfigureServices of the Startup class. This doesn't work because it's too late in the initialization of your application to register a custom IServiceProviderFactory. It should be called before any of the Startup methods get called. In this instance, we'll have to use the ConfigureServices method of IWebHostBuilder instead.

Conclusion

Overall I think this new approach to 3rd party DI integration is much nicer for the application developer.

I actually thought the way this feature was introduced was pretty interesting, so I went digging through the code in GitHub. If you'd like to see where some of this black magic happens, take a look at the StartupLoader class in the Hosting repo.