Autofac ContainerBuilder in ASP.NET Core - What You Need To Know (Part 2)

There are many ways to manage dependency injection inside our applications, and I think it's important to understand the benefits and limitations of different approaches. Using an Autofac ContainerBuilder in ASP.NET Core as the primary way to structure your dependencies instead of using the suggested AutofacServiceProviderFactory is one such path we can explore!

In this article, I highlight how to use Autofac's ContainerBuilder in your ASP.NET Core application instead of the AutofacServiceProviderFactory. We'll look at what you can and cannot do with this approach, versus the other approaches we have access to with dependency injection.

This will be part of a series where I explore dependency resolution with Autofac inside of ASP.NET Core. I’ll be sure to include the series below as the issues are published:

At the end of this series, you’ll be able to more confidently explore plugin architectures inside of ASP.NET Core and Blazor — which will be even more content for you to explore. Keep your eyes peeled on my Dometrain courses for a more guided approach to these topics later in 2023.

Remember to check out these platforms:

// FIXME: social media icons coming back soon!


The Problem With AutofacServiceProviderFactory

To be fair, the section title is *almost* click-bait. I do think that AutofacServiceProviderFactory being used as the suggested way to set up Autofac in your ASP.NET Core applications is great for most applications. The great majority of developers who want to use Autofac as their dependency injection framework of choice would not run into many issues at all this way.

It does afford us the ability to:

  • Access the WebApplicationBuilder (and anything available at this point in time)
  • Access to the instance of IConfiguration (also available off the WebApplicationBuilder instance)
  • Ability to pass dependencies onto minimal APIs

But the big issue for me: We can't access the WebApplication instance. When I build plugin architectures in C#, in particular building ASP.NET Core applications, I like to have access to the WebApplication instance in order to register routes. This allows me to register minimal APIs from my plugins with ease, which technically only need access to an implementation of IEndpointRouteBuilder to get the handy syntax.

Can I register non-minimal APIs without this? Absolutely. Is there another way to provide similar syntax and not require a WebApplication instance? Very likely. But instead of trying to work around THAT problem, I wanted to see if I could just get access to the dependency I am interested in.

It was time to change the plan on how to set up my dependency container!


Exploring A Sample ASP.NET Core Application

Let's look at a sample application so that we have some common ground to explore. If you've read the previous article, this will look similar -- a variation of the sample weather application:

using Autofac;

using Microsoft.AspNetCore.Mvc;

// personal opinion:
// I absolutely love having the entry point of my
// applications being essentially:
// - make my dependencies
// - give me the primary dependency
// - use it
// - ... nothing else :)
ContainerBuilder containerBuilder = new();
containerBuilder.RegisterModule<MyModule>();

using var container = containerBuilder.Build();
using var scope = container.BeginLifetimeScope();
var app = scope.Resolve<WebApplication>();
app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

internal sealed class MyModule : Module
{
    protected override void Load(ContainerBuilder containerBuilder)
    {
        containerBuilder.RegisterType<DependencyA>().SingleInstance();
        containerBuilder.RegisterType<DependencyB>().SingleInstance();
        containerBuilder.RegisterType<DependencyC>().SingleInstance();
        containerBuilder
            .Register(ctx =>
            {
                var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs());
                return builder;
            })
            .SingleInstance();
        containerBuilder
            .Register(ctx => ctx.Resolve<WebApplicationBuilder>().Configuration)
            .As<IConfiguration>()
            .SingleInstance();
        containerBuilder
            .Register(ctx =>
            {
                var builder = ctx.Resolve<WebApplicationBuilder>();

                var app = builder.Build();
                app.UseHttpsRedirection();

                // FIXME: the problem is that the Autofac ContainerBuilder
                // was used to put all of these pieces together,
                // but we never told the web stack to use Autofac as the
                // service provider.
                // this means that the minimal API will never be able to
                // find services off the container. we would need to resolve
                // them BEFORE the API is called, like in this registration
                // method itself, from the context that is passed in.
                //DependencyA dependencyA = ctx.Resolve<DependencyA>();

                // FIXME: But... What happens if something wants to take a
                // dependency on the WebApplication instance itself? Once the
                // web application has been built, there's no more adding
                // dependencies to it!

                var summaries = new[]
                {
                    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
                };

                app.MapGet(
                    "/weatherforecast",
                    (
                        [FromServices] DependencyA dependencyA
                      , [FromServices] DependencyB dependencyB
                      , [FromServices] DependencyC dependencyC
                    ) =>
                    {
                        var forecast = Enumerable
                            .Range(1, 5)
                            .Select(index => new WeatherForecast
                            (
                                DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                                Random.Shared.Next(-20, 55),
                                summaries[Random.Shared.Next(summaries.Length)]
                            ))
                            .ToArray();
                        return forecast;
                    });

                return app;
            })
            .SingleInstance();
    }
}

internal sealed class DependencyA(
    WebApplicationBuilder _webApplicationBuilder);

internal sealed class DependencyB(
    WebApplication _webApplication);

internal sealed class DependencyC(
    IConfiguration _configuration);

One callout of this approach is that using the Autofac ContainerBuilder class as our primary dependency container affords us the opportunity to structure our entry point to just:

  • Container creation
  • Dependency registration
  • Primary dependency resolution
  • ... Call a single method to start the app up!

This is, in my opinion, ideal application startup code. Why? Because you never need to come back here to touch it. Ever. No matter how many things you add in! And that's all because you can scan assemblies to load more modules.

Again, this is a personal preference of mine and I am not trying to claim this should be everyone's goal.

The Flaws of Autofac ContainerBuilder in ASP.NET Core

Of course, another approach that isn't quite bulletproof. So let's discuss what we DON'T get with this setup of Autofac:

  • Service-resolved parameters passed on minimal APIs simply don't work. The WebApplication that was built is not configured to use Autofac as the dependency injection framework!
  • Like the previous article, we still can't get the WebApplication instance on the dependency container... So we didn't make any advancements specifically on accessing that.

But that's mostly it! It's not a terrible list of drawbacks, but the Autofac ContainerBuilder approach was not a silver bullet solution for us. So what did we get out of it? This video on Autofac will also help explain:

The Benefits of Autofac ContainerBuilder in ASP.NET Core

Pros and cons for everything we do! Now that we've seen the issues with Autofac ContainerBuilder in ASP.NET Core, it's time to look at what advancements we got out of this:

  • We can still access the WebApplicationBuilder and IConfiguration instances, so that's a comparable benefit to using the AutofacServiceProviderFactory approach.
  • We can get a very streamlined entry point to our program, which I really like to see. Container creation, registration, resolve your entry point method, and that's all!
  • Minimal APIs work, but not with dependencies. Still, we can pre-resolve the dependencies the minimal APIs want and pass those in at the time of method registration. See the commented code!

More Inversion for Plugin-Based ASP.NET Routes?

We saw that we could register minimal APIs within an Autofac registration method, but unfortunately, we cannot resolve dependencies from the container directly on the minimal API call itself. We could go build a dedicated class like the following that handles route definitions with dependencies being resolved automatically:

internal sealed class WeatherForecastRoutes(
    DependencyA _dependencyA
    // FIXME: still can't depend on this because
    // we can't get the WebApplication
//, DependencyB _dependencyB 
  , DependencyC _dependencyC)
{
    private static readonly string[] _summaries = new[]
    {
        "Freezing", "Bracing", // ...
    };

    public WeatherForecast[] Forecast()
    {
        var forecast = Enumerable.Range(1, 5).Select(index =>
            new WeatherForecast
            (
                DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                Random.Shared.Next(-20, 55),
                _summaries[Random.Shared.Next(_summaries.Length)]
            ))
            .ToArray();
        return forecast;
    }
}

The automatic resolution happens if we have this class AND the dependencies all on the same container. Then it's just a matter of calling this code:

var weatherForecastRoutes = ctx.Resolve<WeatherForecastRoutes>();
app.MapGet("/weatherforecast2", weatherForecastRoutes.Forecast);

This still sucks a bit from a plugin perspective because we'd need to go resolve all of the route classes manually just to call the registration code like that -- all stemming from the fact that these things can't resolve their own access to the WebApplication instance.

But wait... What if we flip things around? What if we could resolve some interface like IRegisterRoutes and pass in the WebApplication instance?!

// NOTE: make sure to register WeatherForecastRouteRegistrar on the
// autofac container as IRegisterRoutes!

internal interface IRegisterRoutes
{
    void RegisterRoutes(WebApplication app);
}

internal sealed class WeatherForecastRouteRegistrar(
    WeatherForecastRoutes _weatherForecastRoutes) :
    IRegisterRoutes
{
    public void RegisterRoutes(WebApplication app)
    {
        app.MapGet("/weatherforecast2", _weatherForecastRoutes.Forecast);
    }
}

// TODO: add this to the autofac code where the 
// WebApplication instance is built:
foreach (var registrar in ctx.Resolve<IEnumerable<IRegisterRoutes>>())
{
    registrar.RegisterRoutes(app);
}

Now we don't even need to care if the WebApplication instance is accessible to our plugins! Maybe the first version wasn't so limiting after all? Maybe we're onto something here... But the next article should explain this in more detail.


Wrapping Up Autofac ContainerBuilder in ASP.NET Core

In this article, I explored using an Autofac ContainerBuilder explicitly instead of using AutofacServiceProviderFactory as is normally suggested. We saw some similar benefits and drawbacks, but also a different set of things to consider. Each way can offer pros and cons depending on what you're after in your application.

What was interesting was that if we're trying to work towards plugins, we might not even need to access the WebApplication instance from our plugins at all! If we care about minimal APIs, this might still be limiting... but otherwise, we're onto an interesting line of thinking!

If you found this useful and you're looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube! Meet other like-minded software engineers and join my Discord community!

Affiliations

These are products & services that I trust, use, and love. I get a kickback if you decide to use my links. There’s no pressure, but I only promote things that I like to use!

  • BrandGhost: My social media content and scheduling tool that I use for ALL of my content!
  • RackNerd: Cheap VPS hosting options that I love for low-resource usage!
  • Contabo: Affordable VPS hosting options!
  • ConvertKit: The platform I use for my newsletter!
  • SparkLoop: Helps add value to my newsletter!
  • Opus Clip: Tool for creating short-form videos!
  • Newegg: For all sorts of computer components!
  • Bulk Supplements: Huge selection of health supplements!
  • Quora: I answer questions when folks request them!

Frequently Asked Questions: Autofac ContainerBuilder in ASP.NET Core

What is Autofac ContainerBuilder?

Autofac ContainerBuilder is a class in the Autofac library that helps configure and initialize an inversion of control container. It is used to register dependencies, manage their lifetimes, and build the container that resolves these dependencies for ASP.NET Core applications.

How does Autofac ContainerBuilder differ from AutofacServiceProviderFactory?

While both are used for managing dependencies in ASP.NET Core, Autofac ContainerBuilder offers more flexibility in dependency registration and resolution. AutofacServiceProviderFactory, on the other hand, integrates Autofac with ASP.NET Core's built-in dependency injection system, providing a straightforward approach for using Autofac as the DI container.

What are the benefits of using Autofac ContainerBuilder in ASP.NET Core?

Benefits include detailed control over dependency registration and resolution, enabling complex dependency management scenarios. It allows a streamlined application setup where the container creation, registration, and primary dependency resolution are all centralized, simplifying the startup process.

What are the limitations of using Autofac ContainerBuilder in ASP.NET Core?

One major limitation is that minimal APIs cannot resolve dependencies directly from the container built with ContainerBuilder if Autofac is not set as the service provider. Additionally, it cannot access the `WebApplication` instance directly for registering routes, which may limit its use in certain plugin architectures.

Can Autofac ContainerBuilder be used for plugin architectures in ASP.NET Core?

Yes, it can be used for plugin architectures, but with some caveats. While it facilitates the registration of dependencies and services within a plugin, accessing the `WebApplication` instance directly is not supported, which can complicate the integration of plugins that need to register routes or perform similar tasks.

AutofacServiceProviderFactory in ASP.NET Core - Problems With Dependency Injection (Part 1)

Learn how to use AutofacServiceProviderFactory in ASP.NET Core for dependency injection! This article explains how to set it up along with the pros and cons.

Autofac In ASP NET Core - How To Avoid This Debugging Nightmare

Learn how to use Autofac in ASP NET Core applications with only a couple of lines of code! Avoid this common problem when setting up dependency injection.

Autofac ComponentRegistryBuilder in ASP.NET Core - How To Register Dependencies (Part 3)

Learn how to use Autofac ComponentRegistryBuilder in ASP.NET Core! We'll see how we can move closer to getting the C# plugin architecture support we want!

An error has occurred. This application may no longer respond until reloaded. Reload x