Understanding .NET Generic Host Model
Tags: dotnet, csharp, tutorial
Published: 2020-08-13 08:00:29

---
title: Understanding .NET Generic Host Model
published: true
date: 2020-08-13 08:00:00 UTC
tags: dotnet, csharp, tutorial
canonical_url: https://sahansera.dev/dotnet-core-generic-host/
cover_image: https://sahansera.dev/static/837e74644caafaa1f39f6d5d105602be/d170a/dotnet-core-generic-host.jpg
---

In this article, we will concentrate on how the Generic Host model hosts ASP.NET Core 3.x Web app and a Worker Service. We will first discuss the definition of a _Host_ and its configuration. In the subsequent sections, we will dive into the implementation details from a higher level.

### So what’s the deal with the Generic Host

With the separation of execution and initialisation, Generic Host provides us with a cleaner way to configure and start up our apps. By default, when you create an ASP.NET Core app now, your application will be hosted using the Generic Host model. If you create a new worker service app, it will be hosted the same way.

Not only that, but this model also provides you standardised configuration, DI, logging, and many more. You can even create a traditional console app, beef it up and make use of Generic Host.

> 💡 Follow along with the code from this [repository](https://github.com/sahan91/dotnet-generic-host-example)

### The Host

According to the official documentation, a `Host` is,

> ASP.NET Core apps configure and launch a host. The host is responsible for app startup and lifetime management. At a minimum, the host configures a server and a request processing pipeline. The host can also set up logging, dependency injection, and configuration.

Let’s create a new .NET 3.1 WebAPI and a Worker Service project

```bash
dotnet new webapi -n WebApplication
dotnet new worker -n WorkerService
dotnet new sln
dotnet sln add WebApplication WorkerService
```

If you open up the solution in an IDE, you will see the following project structure.

![dotnet-core-generic-host-1.png](https://sahansera.dev/static/7574402368b68f39b8626bb1f9bc8f39/0c69d/dotnet-core-generic-host-1.png "dotnet-core-generic-host-1.png")

They both have a `Program.cs` which takes care of setting up a host. In the case of the WebApplication project, it sets up a request processing pipeline defined in a `Startup.cs` and in the WorkerService project, sets a new hosted service which is an essentially an `IHostedService`.

In the WebApplication project, when you open up the Program.cs file, you will find the following boilerplate code has been added by the template:

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

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

And, in the WorkerService project we have the following code:

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

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        });
```

Except for the `ConfigureWebHostDefaults()` and `ConfigureServices()`, everything else is the same.

### Host Configuration

If you look at the `CreateHostBuilder` method in the above code, it calls a `CreateDefaultBuilder` static method from [Host](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs) coming from [Microsoft.Extensions.Hosting](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder?view=dotnet-plat-ext-3.1) namespace. It looks like that when we scaffold an ASP.NET Core app, it gives us a _.NET Generic Host_ by default now. We used to have Web Host in ASP.NET Core 2.x, which was made deprecated since ASP.NET Core 3.0. For any future applications, it is recommended to use the .NET Generic Host.

This does a few things under the covers by wrapping,

- Dependency Injection services
- HTTP Server implementation (such as Kestrel)
- Logging
- Configuration etc.

In order to get an idea of what the above methods do, I looked into the [source code on Github](https://github.com/dotnet/runtime/blob/master/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs).

We will start off with CreateDefaultBuilder method first.

**[Host.CreateDefaultBuilder()](https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs)**

```csharp
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
    // Initialize a new HostBuilder object
    var builder = new HostBuilder();

    // Specify the content root directory
    builder.UseContentRoot(Directory.GetCurrentDirectory());

    // Host Configuration : Add environment variables starting with DOTNET_
    // and add any command line args passed
    builder.ConfigureHostConfiguration(config => ... );

    // App Configuration : Add appsettings.json (depending on the env.) files
    // and add user secrets if in development mode
    builder.ConfigureAppConfiguration((hostingContext, config) => ... )

    // Config logging
    .ConfigureLogging((hostingContext, logging) => ... )

    // Use default DI provider
    .UseDefaultServiceProvider((context, options) => ... );

    return builder;
}
```

As you can see, it pretty much configures a `HostBuilder` object and returns it. There’s nothing really specific to web hosting in here. This is why it’s common to both HTTP and non-HTTP workloads.

Taking a step further, let’s look at how the web host gets configured. We will now look through `ConfigureWebHostDefaults` method.

**[GenericHostBuilderExtensions.ConfigureWebHostDefaults()](https://github.com/dotnet/aspnetcore/blob/4d4f2fe00ffa7c1b9ea08164f9f26a51a392d007/src/DefaultBuilder/src/GenericHostBuilderExtensions.cs#L26)**

```csharp
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
    return builder.ConfigureWebHost(webHostBuilder =>
    {
        WebHost.ConfigureWebDefaults(webHostBuilder);

        configure(webHostBuilder);
    });
}
```

Remember that `ConfigureWebHostDefaults` is used only for HTTP workloads and let’s see what we get as the default web host configuration.

**[WebHost.ConfigureWebDefaults()](https://github.com/dotnet/aspnetcore/blob/4d4f2fe00ffa7c1b9ea08164f9f26a51a392d007/src/DefaultBuilder/src/WebHost.cs#L215)**

```csharp
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
	// Configure static web assets if in Development mode
    builder.ConfigureAppConfiguration((ctx, cb) => ... );

	// Configure Kestrel
    builder.UseKestrel((builderContext, options) => ... )

	// Configure the default services
    .ConfigureServices((hostingContext, services) => ... )

	// Configure IIS for Windows
    .UseIIS()
    .UseIISIntegration();
}
```

So far, we have seen that both approaches use the same Generic Host paradigm in the two projects. If you are interested in customising the default configuration, head over to [Microsoft Docs’ official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1#host-configuration).

### Finally, how does it all run?

Now comes the interesting part.

In both cases, after the configuration sections, we finally call the `Run()` on `IHost` object implemented in `HostingAbstractionsHostExtensions`. This will run the app and block the calling thread until the host is shut down. This is enabled by `WaitForShutdownAsync` which is called at the beginning of the start-up process, which can be triggered by Ctrl+C/SIGTERM or SIGINIT.

Let’s look at how both web hosts and worker services run.

For a worker service, remember how we registered our Worker class by passing it into `ConfigureServices` method. This Worker class extends [`BackgroundService`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice?view=dotnet-plat-ext-3.1) which in turn implements [`IHostedService`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.ihostedservice?view=dotnet-plat-ext-3.1). `IHostedService` provides 2 methods, namely, `StartAsync` and `StopAsync`. So when we run our host, it must be retrieving our Worker service and invoking these methods.

![dotnet-core-generic-host-2.png](https://sahansera.dev/static/62f75431b28332dbbf2b7b1d69754ff8/5a190/dotnet-core-generic-host-2.png "dotnet-core-generic-host-2.png")

_Source: [Microsoft](https://devblogs.microsoft.com/cesardelatorre/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/)_

In the `Host.cs` there’s a separate `StartAsync` method and we can find the following lines inside it.

```csharp
_hostedServices = Services.GetService<IEnumerable<IHostedService>>();

foreach (var hostedService in _hostedServices)
{
    // Fire IHostedService.Start
    await hostedService.StartAsync(combinedCancellationToken).ConfigureAwait(false);
}
```

So our guess was correct. It certainly invokes the `StartAsync` method of `BackgroundService`, that calls `ExecuteAsync` method in which we have ultimately implemented in our `Worker` class.

For a web host, there’s a little bit of abstraction on top of this before it hits the above section. A summary of how it reaches this as follows;

1. In Program.cs, configure a new webhost builder object in ConfigureWebHostDefaults
2. Register Startup class
3. `GenericHostBuilderExtensions.ConfigureWebHostDefaults` method gets called
4. `GenericHostWebHostBuilderExtensions.ConfigureWebHost` gets called
5. Register a `GenericWebHostService` service

```csharp
public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
    var webhostBuilder = new GenericWebHostBuilder(builder);
    configure(webhostBuilder);
    builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
    return builder;
}
```

So what is a `GenericWebHostService` ? It’s an `IHostedService` 🤩. Rest of the story is as above as we looked at in the worker service scenario. Because of this nicely decoupled initialisation we are able to run both ASP.NET Core and Worker services on the Generic Host.

### Summary

To summarise, we looked at what makes the Generic Host _generic_ and dug deeper into the implementation details in .NET Github repo. We also looked at what makes an ASP.NET Core web application and a worker service different, configuration-wise. This post became a bit longer than I initially I thought it would be 😅 Nevertheless, hope you picked up a thing or two.

Cheers!

### References

1. [https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio)
2. [https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1)
3. [https://andrewlock.net/exploring-the-new-project-file-program-and-the-generic-host-in-asp-net-core-3/](https://andrewlock.net/exploring-the-new-project-file-program-and-the-generic-host-in-asp-net-core-3/)