Where should I perform custom application initialization steps in ASP.NET Core?

You can add an extension method for IWebHost (instead of IWebHostBuilder) and then use IWebHost.Services for resolving services. Here's an example of how to retrieve IConfiguration:

public static class WebHostExtensions
{
    public static IWebHost SomeExtension(this IWebHost webHost)
    {
        var config = webHost.Services.GetService<IConfiguration>();

        // Your initialisation code here.
        // ...

        return webHost;
    }
}

Usage of this extension method would look something like this:

CreateWebHostBuilder(args)
    .Build()
    .SomeExtension()
    .Run();

If you need an async version of SomeExtension, you can split up the chaining above and await the extension method. Here's what that might look like:

public static async Task SomeExtensionAsync(this IWebHost webHost)
{
    var config = webHost.Services.GetService<IConfiguration>();

    // Your initialisation code here with awaits.
    // ...
}

Usage looks something like this:

public static async Task Main(string[] args)
{
    var webHost = CreateWebHostBuilder(args)
        .Build();

    await webHost.SomeExtensionAsync();

    webHost.Run();
}

Also, can I be sure that this code will be executed after Startup.ConfigureServices() when all dependencies are registered?

With the approach I've outlined above, the answer here is yes.


Note that IWebHost.Services represents the root IServiceProvider, which will not support resolving scoped instances. IConfiguration is a singleton, so this isn't an issue for that, but if you have scoped dependencies, you'll need to create an explicit scope inside of your extension method.


In Program.cs you have the following code for your Main method:

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

After the Build() part has run, you have a fully configured host. As such, you can simply do something like the following:

var host = CreateWebHostBuilder(args).Build();

// do something with host

host.Run();

The host has a member, Services, which is an instance of IServiceProvider, so you can pull any services you need from that, i.e.

var config = host.Services.GetRequiredService<IConfiguration>();

Just bear in mind that at this point, there is no inherent scope, so if you need scoped services, you'll need to create one:

using (var scope = host.Services.CreateScope())
{
    var myScopedService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
    // do something with myScopedService
}