Mark
Mark

Reputation: 11750

How do I host a WebApplication and a BackgroundService in the same application?

I have a command-line application (similar to what would be created with the dotnet new worker command) that hosts a BackgroundService implementation, like this:

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services => {
        services.AddHostedService<MyBackgroundService>();
        services.AddSingleton<IMyType>(myinstance);
        // and so on...
    }).Build();

await host.RunAsync();

Now, I would like this application to also host a web api. Currently, I have a whole separate builder that I'm instantiating inside the MyBackgroundService class, with a separate set of services/singletons/whatever:

var builder = WebApplication.CreateBuilder();

// ... various web stuff...

var webApi = builder.Build();

// ... more web api stuff...

await webApi.StartAsync(cancellationToken);

I'd like to set this up to share a single DI container... how do I do that? My web api uses a WebApplicationBuilder, while my BackgroundService uses a DefaultBuilder (which is a IHostBuilder). Is there any way to set this up elegantly?

Edit: I found this: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0 which gives me the ConfigureWebHostDefaults method for the generic host, but the example isn't complete. I can put the AddControllers() and AddRouting() etc on the generic host, but once I call Build() I can't do things like UseHttpLogging() or UseSwagger() or MapControllers().

Upvotes: 6

Views: 3209

Answers (1)

nahammel
nahammel

Reputation: 606

Assuming you're starting from a console app and want to add in the API portion, you can do something like:

await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<SomeBackgroundService>();
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder
            .ConfigureServices(services =>
            {
                services.AddControllers();
                services.AddEndpointsApiExplorer();
                services.AddSwaggerGen();
            })
            .Configure((hostContext, app) =>
            {
                if (hostContext.HostingEnvironment.IsDevelopment())
                {
                    app.UseSwagger();
                    app.UseSwaggerUI();
                }
​
                app.UseHttpsRedirection();
                app.UseAuthorization();
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            });
        
           //can optionally use a startup file similar to what older versions of .NET used
        //webBuilder.UseStartup<Startup>();
    })
    .Build()
    .RunAsync();
}

Also, assuming you started from a console app, you may need to add:

<ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

to your csproj, as that will pull in the additional web options.

Edit: It appears the services specific to the web context need to be registered within the webBuilder.

Upvotes: 7

Related Questions