Understanding CreateBuilder() method in AspNetCore (.NET 7)

Understanding CreateBuilder() method in AspNetCore (.NET 7)

Introduction

In order to understand the functionality of the CreateBuilder() method we will have to go deep into the AspNetCore source code on Github and debug the code from a sample WebAPI project. Although this is not a mandatory requirement but doing so would give you a better clarity of what is happening under the hood.

You can find the source code of Microsoft.AspNetCore from the Github Repository. If you are interested to find out How to setup and debug the Microsoft AspNetCore source code you can refer to my previous post from the link below.

Setup and debug microsoft aspnetcore source code

Program.cs in Microsoft.AspNetCore project

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

The very first line of the Program.cs starts with the call to the CreateBuilder() method of the WebApplication class. The call to the CreateBuilder() method returns us an instance of the WebApplicationBuilder class. You can find the WebApplication, WebApplicationBuilder and the related classes in the figure below.

aspnetcore solution

WebApplication class

The WebApplication class is a sealed class and implements the following interfaces.

IHost
IApplicationBuilder
IEndpointRouteBuilder
IAsyncDisposable

This class is used to configure the http pipleline and the routes.

There are multiple overloads of the CreateBuilder() method in the WebApplication class that returns an instance of the WebApplicationBuilder class with preconfigured defaults.

    public static WebApplicationBuilder CreateBuilder(string[] args) =>
        new(new() { Args = args });

There is also a Create() method that just returns an instance of the WebApplication class instead of the WebApplicationBuilder class.

public static WebApplication Create(string[]? args = null) =>
        new WebApplicationBuilder(new() { Args = args }).Build();

The Create() method internally invokes the Build() method of the WebApplicationBuilder class and returns a WebApplication instead. However in our Program.cs class you would find that we are explicitly calling the Build() method after injecting the required services to give us our app instance.

var app = builder.Build();

WebApplicationBuilder class

Just like the WebApplication class the WebApplicationBuilder is also a sealed class but unlike WebApplication it does not implement any interface.

The constructor of the WebApplicationBuilder does lots of important tasks that is important to understand. The code snippet below from the microsoft source code show the Constructor of the WebApplicationBuilder class.

WebApplicationBuilder Constructor

internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilder>? configureDefaults = null)
    {
        var configuration = new ConfigurationManager();

        configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_");

        _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
        {
            Args = options.Args,
            ApplicationName = options.ApplicationName,
            EnvironmentName = options.EnvironmentName,
            ContentRootPath = options.ContentRootPath,
            Configuration = configuration,
        });

        // Set WebRootPath if necessary
        if (options.WebRootPath is not null)
        {
            Configuration.AddInMemoryCollection(new[]
            {
                new KeyValuePair<string, string?>(WebHostDefaults.WebRootKey, options.WebRootPath),
            });
        }

        // Run methods to configure web host defaults early to populate services
        var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);

        // This is for testing purposes
        configureDefaults?.Invoke(bootstrapHostBuilder);

        bootstrapHostBuilder.ConfigureWebHostDefaults(webHostBuilder =>
        {
            // Runs inline.
            webHostBuilder.Configure(ConfigureApplication);

            webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
            webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
            webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
            webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
        },
        options =>
        {
            // We've already applied "ASPNETCORE_" environment variables to hosting config
            options.SuppressEnvironmentConfiguration = true;
        });

        // This applies the config from ConfigureWebHostDefaults
        // Grab the GenericWebHostService ServiceDescriptor so we can append it after any user-added IHostedServices during Build();
        _genericWebHostServiceDescriptor = bootstrapHostBuilder.RunDefaultCallbacks();

        // Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder. Then
        // grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection.
        var webHostContext = (WebHostBuilderContext)bootstrapHostBuilder.Properties[typeof(WebHostBuilderContext)];
        Environment = webHostContext.HostingEnvironment;

        Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
        WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
    }

The first thing that happens inside the contructor is creating an instance of the ConfigurationManager() class. The ConfigurationManager class is present in the Microsoft.Extenstions.Configuration.dll. This class allows us to specify the configuration of the application. The default instance has the Sources property defined as the MemoryConfigurationSource. On calling the below mentioned code EnvironmentVariablesConfigSource is added to the Sources in the constructor.

configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_");
{Microsoft.Extensions.Configuration.Memory.MemoryConfigurationSource}
{Microsoft.Extensions.Configuration.EnvironmentVariables.EnvironmentVariablesConfigurationSource}

The AddEnvironmentVariables() method is an extension method present inside the EnvironmentVariablesExtensions.cs class inside the library Microsoft.Extensions.Configuration.EnvironmentVariables.dll. This is the method that adds the
EnvironmentVariablesConfigurationSource to the config Sources property.
The EnvironmentVariablesConfigurationSource has a property for Prefix that is used to filter the EnvironmentVariables based on the prefix.

In the code above we are adding ASPNETCORE_ as the prefix since we are building a WebApplicationBuilder object. Although later in the code when creating an instance of the hostApplicationBuilder the default settings from the generic host is applied and DOTNET_ prefix is added to the EnvironmentVariablesConfigurationSource.

After configuration an instance of HostApplicationBuilder is then created and it is assigned the values of the arguments passed, the configuration instance and the WebApplicationOptions values.

This is the time when all the defaults settings of the Generic Host gets shipped to the HostApplicationBuilder instance.

  1. Args
  2. ApplicationName
  3. EnvironmentName
  4. ContentRootPath
  5. Configuration
 _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
        {
            Args = options.Args,
            ApplicationName = options.ApplicationName,
            EnvironmentName = options.EnvironmentName,
            ContentRootPath = options.ContentRootPath,
            Configuration = configuration,
        });

The HostApplicationBuilder class is present in the Microsoft.Extensions.Hosting.dll and it returns an instance with all required services injected along with the properties which an Generic Application Host should have.

        var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder);

        // This is for testing purposes
        configureDefaults?.Invoke(bootstrapHostBuilder);

        bootstrapHostBuilder.ConfigureWebHostDefaults(webHostBuilder =>
        {
            // Runs inline.
            webHostBuilder.Configure(ConfigureApplication);

            webHostBuilder.UseSetting(WebHostDefaults.ApplicationKey, _hostApplicationBuilder.Environment.ApplicationName ?? "");
            webHostBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, Configuration[WebHostDefaults.PreventHostingStartupKey]);
            webHostBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, Configuration[WebHostDefaults.HostingStartupAssembliesKey]);
            webHostBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, Configuration[WebHostDefaults.HostingStartupExcludeAssembliesKey]);
        },
        options =>
        {
            // We've already applied "ASPNETCORE_" environment variables to hosting config
            options.SuppressEnvironmentConfiguration = true;
        });

BootstrapHostBuilder class is an implementation of the IHostBuilder interface an it provides an instance to add capabilities of WebHosting to the IHostBuilder by invoking the ConfigureWebHostDefaults() method.

The ConfigureWebHostDefaults() is an extension method present inside the GenericHostBuilderExtensions class in Microsoft.Extensions.Hosting to configure the Web Host Defaults.

The ConfigureWebHostDefaults() method internally invokes the ConfigureWebDefaults() method of the WebHost class by passing the arguments to ConfigureWebDefaults() method and returns an instance with the web hosting properties.

It is inside the ConfigureWebDefaults() method that the server capabilities like using Kestrel, adding Routing and using IIS and IIS Integration capabilities is added to the builder instance.

Once the builder instance is loaded with the WebHosting capabilities and injected with all the default services needed for a WebApplicationBuilder other services related to the Application are further added to the services in the builder object in the Program.cs.

The sample code from Program.cs demonstrates the same.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

After adding all the required services to the builder instance the Build() method is invoked on the object to return a WebApplication object to further configure the pipleline.

The sample code below from Program.cs shows how the Build() method is invoked and the pipeline is configured.

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

After the Run() method is executed our WebApplication is all set to listen to the HTTP Request and process the request to give the HTTP Response back.

Conclusion

In this post we explored the different operations are performed inside the CreateBuilder() method of the Program.cs file in AspNetCore 7 WEbAPI Application.

To summarize ,

  1. The WebApplication class calls the CreateBuilder() method to return a WebApplication Builder.
  2. The WebApplicationBuilder creates an instance of the ConfigurationManager class and adds relevant configuration to the instance.
  3. The HostApplicationBuilder is then built with the configuration and other properties specified.
  4. The Web Hosting capabilities of the server are then added to the Host Application builder to setup Kestrel, Add Routing, setup IIS and IISIntegration.
  5. Additional services are added to the WebApplication builder in the application code inside Program.cs.
  6. The WebApplication builder calls the Build() method to return an instance of the WebApplication.
  7. The Web Application object configures the http pipeline and then calls the Run() method to keep the process running and to be ready to listed to the HTTP Request and processing it to return the HTTP Response.

Although I have tried my level best to explain what is happening behind in the Program.cs the code would be more clear if you set up and debug the source code from your sample web application.

I hope you might have gained some undertsanding of how things work under the hood and would be excited to explore further.