How to Use Easy, Low Cost Startup Class with ASP.NET 6 Minimal Hosting Model

With the .NET Core 2.1 having reached end of life, and the looming end of life dates for .NET 5 (this spring) and .NET Core 3.1 (this fall), a lot of developers are facing migrating their services to .NET 6.0. Depending on the customization level and the sheer scale of your service ecosystem this may be an easy or relatively complicated task – especially if you would like to additionally tap into the new lightweight hosting model around WebApplication type.

A very low-cost, easy approach to this is to take advantage of the fact that one can easily reuse an existing Startup class with the new hosting model too. This allows leaving most of the code intact, and performing only tiny refactorings around the host bootstrapping.

Typical Startup

Consider the following super simple Startup class, which, however, resembles structurally a Startup class that is the backbone of just about every .NET 3.1 and .NET 5.0 application (yes, you could have defined everything inline in Program class too, when bootstrapping the WebHost, but that was not too common):

class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Configuring WebApplicationBuilder

The new WebApplicationBuilder gives us access to everything that is needed by the above Startup class – the readily available IConfiguration instance, the DI container builder in the form of IServiceCollection and the application pipeline IApplicationBuilder. In fact, in the latter case the new WebApplication itself is already an IApplicationBuilder.

As such, all that is needed is a little helper that will bridge the WebApplicationBuilder and the good old Startup:

public static class WebApplicationBuilderExtensions 
{
    public static WebApplication Build<TStartup>(this WebApplicationBuilder builder)
    {
        var startup = Activator.CreateInstance(typeof(TStartup), new[] { builder.Configuration });
        if (startup == null) throw new InvalidOperationException("Could not instantiate Startup!");

        var configureServices = typeof(TStartup).GetMethod("ConfigureServices");
        if (configureServices == null) throw new InvalidOperationException("Could not find ConfigureServices on Startup!");
        configureServices.Invoke(startup, new[] { builder.Services });

        var app = builder.Build();

        var configure = typeof(TStartup).GetMethod("Configure");
        if (configure == null) throw new InvalidOperationException("Could not find Configure on Startup!");
        configure.Invoke(startup, new[] { app });

        return app;
    }
}

Of course the handling of the Startup here is rather naive – though enough for our modest needs – and could easily be hardened, for example using the official StartupLoader as a reference.

Equipped with this, we can now point the WebApplicationBuilder at our Startup and we have managed to successfully marry the two:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build<Startup>();
app.Run();

All in all, this gives us a very easy (“low-budget”) way of migrating an existing app with a Startup to the new minimal hosting model.

 

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *