Card image cap

Exploring ASP.NET Core Fundamentals - Building the Host

ASP.NET Core  • Posted 2 months ago

When an AspNetCore application starts up for execution, the first method to be called is the main() method inside the Program.cs file. Inside the main() method, an AspNetCore application builds a "Host" and invokes Run() method on it.

A typical Main() method from an AspNetCore boilerplate code looks like this:

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) 
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(builder =>
        {
            builder.UseStartup<Startup>()
        });
}

The Host built during the app startup encapsulates all the resources required for the application to run.

The resources include:

  1. Hosted Server configuration
  2. Middleware registrations
  3. Logging providers
  4. Service registrations
  5. Host and App Configuration

Once the Host is built and run, the application starts up and all the above resources are registered and attached to the Host which makes them available for the executing components for use via Dependency Injection. A Host builds up the container which facilitates an AspNetCore application to run.

Configuring the Host:

In the above code snippet, observe that we're calling two methods on top of the Host instance.

  1. CreateDefaultBuilder() and
  2. ConfigureWebHostDefaults()

The CreateDefaultBuilder() method is responsible for configuring the Host with the following:

  1. Sets the ContentRoot:
    • The physical root on the filesystem where the application is being executed
  2. Sets the HostConfiguration:
    • From environmental variables that start with "DOTNET_" and command-line arguments.
    • These are further used in implementing the IHostEnviroment service which provides information about the application environment.
  3. Sets the AppConfiguration:
    • From various sources such as appsettings.json, environmental variables, command-line arguments.
    • This can be called multiple times with new or added configurations.
    • The application picks up the latest AppConfiguration with the last updated value added for any given key.
  4. Logging framework:
    • Push logs generated from the application onto Console, Debug, EventSource and so on by means of ILoggingFactory interface.
    • This can be further extended to use external providers such as Log4Net, Serilog or event Cloud providers such as Azure AppInsights or AWS CloudWatch.

Configuring Host for Web:

To build a Host which runs on HTTP workloads, we call the ConfigureWebHostDefaults() method on the Host. This specifies that the Host shall handle HTTP requests and processes them.

The ConfigureWebHostDefaults() method is responsible for configuring the Host with resources and configurations that help in the application to process HTTP requests.

  1. Sets up the HostConfiguration with environment veriables which start with "ASPNETCORE_"
  2. Configures the application to run on Kestrel webserver - since a web application requires to be deployed on a webserver to accept HTTP requests.
  3. Configures the HostFiltering middleware
  4. Enables IISIntegration() which provides the necessary resources for the application to be hosted on an IIS server instead of the default Kestrel server.
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(builder =>
        {
            builder
                .UseContentRoot(
                    Directory.GetCurrentDirectory())
                .UseKestrel()
                .UseIISIntegration()
                .UseStartup<Startup>()
        });

ConfigureWebHostDefaults() can also be used for registering services and adding middlewares to the pipeline at the time of building the Host itself. Instead of configuring the builder to Use a Startup class, we can ConfigureServices and Configure components as:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseKestrel()
            .UseIISIntegration()
            .ConfigureServices(services =>
            {
                // register services here
            }).Configure(app =>
            {
                // tag to request pipeline here
            });
        });

Configuring the Host for Background Tasks:

While a typical Host is built and configured to run on HTTP workloads by chaining with ConfigureWebHostDefaults() method, it can also be built to perform non-HTTP workloads such as Jobs by configuring as below:

public static IHostBuilder CreateHostBuilder(string[] args) 
{
    return Host.CreateDefaultBuilder(args)
    .ConfigureServices((host, services) => {
        services.AddHostedService<MyService>();
    });
}

Where MyService is a Background service implementing IHostedService interface. It runs inside the Host for a given event trigger (such as a timer for example) and executes a functionality.

Or a Host can be configured to run both, which is also a possibility.

public static IHostBuilder CreateHostBuilder(string[] args) 
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(builder =>
        {
            builder.UseStartup<Startup>()
        })
        .ConfigureServices((host, services) => {
            services.AddHostedService<MyService>();
        });
}

"When we invoke Run() method on the Host, it calls IHostedService.StartAsync() on every IHostedService implementation it finds in the container. For a Host that runs non-Http workloads, it boots up the Backrgound Services registered under the Host for execution. For a web application, a IHostedService implementations is a service that starts a HTTP server (Kestrel or IIS)."

The Sequence of Execution:

When we run an AspNetCore application, the following things happen in sequence:

  1. The AspNetCore runtime looks for a Main method, invokes it.
  2. The Main() method calls the Host.CreateDefaultBuilder() method.
  3. The CreateDefaultBuilder() method
    • sets the content root with the path specified, pulls values from environment variables, and commandline arguments if any and then builds the host IConfiguration.
    • also pulls configuration from appsettings, environment variables and other sources and then builds the IConfiguration from it.
  4. The Main() method then calls the ConfigureWebHostDefaults() method for web application.
  5. The ConfigureWebHostDefaults() method
    • adds up the host IConfiguration with web specific configurations from sources such as environment variables and commandline arguments.
    • configures the application to run on Kestrel with support to run on IIS if needed.
  6. The Main() method then calls the Build() method on the Host instance which is now configured with all the above resources. This results in an IHost object which encapsulates all the resources forming the container for running the application.
  7. By this point - the IConfiguration, IHostEnviroment, IHostApplicationLifetime, IHostLifetime are built and registered to be injected
  8. The Main() method then calls the Run() method on IHost, which starts up the application to listen on the default ports specified in the launchsettings. The application boots up and starts to listen for requests on the host and port untill shutdown.

One can observe that the Host comes up with so much resources to maintain for itself - be it configurations, services or the other components. The basic idea is to have a single instance which owns and maintains all the dependent resources in an application, so that we can have a facade that can have control over app startup and shutdown.

We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept