This article applies to ASP.NET core version 3.0 to 5.0. You can visit program.cs in ASP.NET core to learn the latest version.
In this article, we will look at the role of asp.net core program.cs in more detail. It is the entry point of our app, where we configure the Web host. The program class configures the application infrastructure like Web host, logging, Dependency injection container, IIS integration, etc. It is created, configured, built using the createdefaultbuilder
method of the Main
method in the program class.
Table of Contents
What is Program.cs?
The Program
class contains the method Main
, which is the entry point of the ASP.NET Core applications.
The Main
method is similar to the Main
method of a console Applications. That is because all the .NET Core applications basically are console applications. We build other types of applications like MVC Web Application or Razor page application over the console app.
The main purpose of the Program
class is to configure the applications infrastructure.
The program class creates the Web Host at the startup. it then configures the logging, Dependency Injection Container. configuration system, Kestrel Web server, IIS Integration, etc. It also adds the framework services to the DI container so that we can use it. The code for the program class is automatically generated for us and most probably enough for most of the projects.
What is Web Host?
The Web host is responsible for starting the app and running it. It is created when the application starts. Web host creates a server, which listens for the HTTP requests. It configures the request pipeline (or middleware pipeline). Also, It sets up DI container, where we add our services. Managing the lifetime of the services is also a task of Webhost.
Basically, Web Host ready’s our app to receive the requests. But, the Web Host needs to be created and configured. We do that in Main
method.
Main method
Open the Applications we built in the Hello World Example and open the program.cs
This class has a single method Main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace HelloWorld { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); } } |
This is the entry point to our application.
The following image shows how the web host is created.
CreateWebHostBuilder
creates the host and provides the configuration to it.- The
build
method builds it using the provided configuration - The
Run
method runs it and it then listens for HTTP requests.
Create the Host
CreateWebHostBuilder
The CreateWebHostBuilder
is a static
method, which creates and configures the Host and then returns it.
1 2 3 4 5 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); |
The above syntax is known as “expression bodied function member.” feature that was introduced in C#
The above code is the same as the following function
1 2 3 4 5 6 7 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) { return WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); } |
The method above uses the WebHost helper static class
The CreateDefaultBuilder method of the WebHost helper class is responsible for creating the IWebHostBuilder
instance, with the desired configuration
CreateDefaultBuilder
The CreateDefaultBuilder
performs the following tasks.
- Sets the content root to
Directory.GetCurrentDirectory
. - Loads optional configuration from
Appsettings.json
Appsettings.{Environment}.json
.- User secrets when the app runs in the Development environment.
- Environment variables
- Command-line arguments.
- Enable logging
- Sets up the Dependency Injection Container.
- Configures Kestrel as the webserver
- Adds Framework Services to the DI Container
- Integrates the Kestrel run with
IIS
You can peek into the source code for the CreateDefaultBuilder from the ASP.NET Core meta package
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | public static IWebHostBuilder CreateDefaultBuilder(string[] args) { var builder = new WebHostBuilder(); if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey))) { builder.UseContentRoot(Directory.GetCurrentDirectory()); } if (args != null) { builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); } builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment()) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) .ConfigureLogging((hostingContext, logging) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); }). UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }); ConfigureWebDefaults(builder); return builder; } |
There is a lot of code there. Let us look at each line
Setting the content Root
1 2 3 | builder.UseContentRoot(Directory.GetCurrentDirectory()); |
This sets the content root to Directory.GetCurrentDirectory. This basically sets the current directory as the root of the application.
Loading Configuration file
The next section of the code loads the configuration from the various sources and in the following order
- Appsettings.json.
- appsettings.{Environment}.json.
- User secrets when the app runs in the Development environment.
- Environment variables.
- Command-line arguments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | if (args != null) { builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); } builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment()) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) |
Enable Logging
The code which enables & configures logging follows next.
1 2 3 4 5 6 7 8 9 | .ConfigureLogging((hostingContext, logging) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); }). |
This reads the configuration rules specified in the “Logging” section of the configuration files and configures logging for console and debug output.
Sets up the DI Container
The UseDefaultServiceProvider
method sets up the Dependency Injection container and configures it.
1 2 3 4 5 6 7 | UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }); |
Configures WebHost
Finally, the code calls the ConfigureWebDefaults
method. This method configures the Web Host. The source code is available on GitHub.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | internal static void ConfigureWebDefaults(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((ctx, cb) => { if (ctx.HostingEnvironment.IsDevelopment()) { StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration); } }); builder.UseKestrel((builderContext, options) => { options.Configure(builderContext.Configuration.GetSection("Kestrel")); }) .ConfigureServices((hostingContext, services) => { // Fallback services.PostConfigure<HostFilteringOptions>(options => { if (options.AllowedHosts == null || options.AllowedHosts.Count == 0) { // "AllowedHosts": "localhost;127.0.0.1;[::1]" var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // Fall back to "*" to disable. options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" }); } }); // Change notification services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>( new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) { services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Only loopback proxies are allowed by default. Clear that restriction because forwarders are // being enabled by explicit configuration. options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>(); } services.AddRouting(); }) .UseIIS() .UseIISIntegration(); } |
Kestral
The First method tells the host to use the Kestrel Web server. Kestrel is a cross-platform Managed HTTP server-based. This Server allows ASP.NET Core application runs on OS other than Windows.
1 2 3 4 5 6 | builder.UseKestrel((builderContext, options) => { options.Configure(builderContext.Configuration.GetSection("Kestrel")); }) |
Configure services
This section configures the important services & adds them to DI Container.
1 2 3 4 5 6 | .ConfigureServices((hostingContext, services) => { //Services added here } |
Use IIS Or Integration
Configures IIS Server to Host the App. There are two ways in which you can host the App. One is in process and the other one is out of process.
The in Process runs the app inside the IIS Process and is configured by UseIIS()
.
The Out of process runs in a separate process and uses the Kestrel server. The IIS then acts as a reverse proxy forwarding the requests to the Kestrel. This is configured by the method UseIISIntegration()
.
1 2 3 4 | .UseIIS() .UseIISIntegration(); |
Startup class
The Host is created and configured, but before building & running we need to further configure with the application. We do this in the startup class. By default, the class is named as the startup class. We let the builder know the location of the startup class by using the UseStartup
method.
1 2 3 | .UseStartup<Startup>() |
The startup class contains two methods. One is ConfigureServices
(which is optional) and the other one is Configure
. The ConfigureServices
is where we configure services we created in the app and adds them to the DI Container. In the Configure
method, we create the request pipeline by adding the middlewares.
The CreateWebHostBuilder
will invoke the ConfigureServices
& configure
method from the startup class and configure the host further
Build & Run it
CreateWebHostBuilder
creates the Web Host and returns it back. The Build
& Run
methods are invoked and the app starts and begins listening for HTTP requests.
1 2 3 | CreateWebHostBuilder(args).Build().Run(); |
Summary
The Main
method of the program.cs
class is the entry point of our application. It configures & builds the Web host. The web host is responsible for running our app. Most of the plumbing required to configure host is already done for us in the createdefaultbuilder
method, which is invoked in the Main
method. We can further add our custom configuration is startup class, which we cover in the next tutorial.