Introduction to Program.cs in .NET Core

Introduction to Program.cs in .NET Core

In this post, we take a look at the Program class, its importance in .NET Core project configuration.

·

4 min read

TL;DR

Program.cs is where we configure and run a host from. A host is responsible for running a .NET Core application.

Quick Glance

A minimal Program.cs for a WebAPI project looks like so

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) 
    {
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    }
}

This tells us the following

  1. We have a Main() method inside Program.cs, which acts as the single entry point for the application.
  2. Inside the Main() method we call CreateHostBuilder() to setup a host. Once the setup is complete we Build() and Run() the host.

What is a Host? And why should I care?

A host helps configure, build and run .NET Core applications. It also helps with graceful shutdown, which in-turn helps having a consistent application state. Though most of the configuration is abstracted and already setup, a developer should always know what he/she is dealing with.

There are two different types of hosts in .NET Core

  1. Generic Host
  2. Web Host

Newer versions of .NET Core (3.0 and above) recommend the usage of a generic host. As the name suggests the host is generic and works with both HTTP(example: MVC, WebAPI service) and non-HTTP workloads(example: a background service). Web host on the other hand is meant for HTTP workloads only and still available to support backward compatibility, we can expect it to be removed sooner or later.

Program.cs setup and methods involved

We are going to talk about the generic host since it has become the de-facto type in .NET Core.

Lets look at two minimal ways of setting up a Program.cs

  1. Program.cs for a WebAPI service (HTTP workload)
    public class Program
    {
     public static void Main(string[] args)
     {
         CreateHostBuilder(args).Build().Run();
     }
     public static IHostBuilder CreateHostBuilder(string[] args) 
     {
         Host.CreateDefaultBuilder(args)
             .ConfigureWebHostDefaults(webBuilder =>
             {
                 webBuilder.UseStartup<Startup>();
             });
     }
    }
    
  2. Program.cs for a background worker service (non-HTTP workload)

    public class Program
    {
     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>();
             });
     }
    }
    

Lets take a look at the contained methods

  1. Host.CreateDefaultBuilder() -> This method helps configure content root, app configuration, logging, and dependency injection container. The arguments provided during app launch are set here as configurations and can be used by the application whenever necessary. This can be used by both HTTP and non-HTTP workloads.
  2. IHostBuilder.ConfigureWebHostDefaults() -> Specific to HTTP workloads, this helps setup kestrel, middleware etc.
  3. IHostBuilder.ConfigureServices() -> This method adds services to the DI container. The method can be called multiple times and services will be added to the container additively.
  4. IHostBuilder.Build() -> Initializes the host. This can be called only once.
  5. IHostBuilder.Run() -> Runs the application and blocks the calling thread until the host is shut down. Now if this blocks the calling thread how does the application serve requests?
    Short answer - IHostedServices
    Long answer - In HTTP workloads (an API service in our example), one of the IHostedService implementations (as part of ConfigureWebHostDefaults) is a web service that starts an in-process HTTP server implementation, which continuously listens for HTTP requests and forwards them to the application. For non-HTTP workloads (a background service in our example), the IHostedService implementation called Worker is added to the container. This class derives from the BackgroundService abstract class which has logic to run a task in the background. BackgroundService internally implements the IHostedService interface. When a host starts, it makes sure to run/start each and every implementation of IHostedService available to it.

The new generic host builder does a pretty good job at abstracting away all setup by providing separate logical methods. This logical separation introduces reusability, and we can plug the methods to different types of application in the .NET world.

Thank you for going through this post. I hope you found it useful. Cheers!

Additional resources

  1. Official documentation on GenericHost https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host
  2. More about background tasks and hosted services https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services
  3. A good read around older WebHost and its setup https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2