Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference when registering an IHostedService in .NET Core 3.+ in the startup.cs ConfigureServices or in program.cs Main method?

I'm playing around with trying to use IHostedServices in a 3.1 .NET Core web application.

I can see that I can register them in 2 different places:

  • program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        .ConfigureServices(services =>
        {
            services.AddHostedService<BackgroundServiceA>();
            services.AddHostedService<BackgroundServiceB>();
        });

or in the ConfigureServices method in startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddHostedService<BackgroundServiceA>();
}

I'm under the impression that both of these are identical. And if they are, does one override the other, if both exist?

like image 599
Pure.Krome Avatar asked Oct 12 '25 08:10

Pure.Krome


1 Answers

I'm under the impression that both of these are identical.

Yes, both approaches inject a delegate into the logic used to build an IServiceProvider. Whether you do this via the IHostBuilder or the Startup class, the result is the same.

Does one override the other, if both exist?

The answer to this depends on two things:

  1. The order in which you've registered the delegates.
  2. The details of the registration itself.

To make it easier to explain, here's the code copied from your question:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        .ConfigureServices(services =>
        {
            services.AddHostedService<BackgroundServiceA>();
            services.AddHostedService<BackgroundServiceB>();
        });
public void ConfigureServices(IServiceCollection services)
{
     services.AddHostedService<BackgroundServiceA>();
}

This shows an order that goes like this:

  1. Startup.ConfigureServices, because ConfigureWebHostDefaults is called first.
  2. IHostBuilder.ConfigureServices

This means the registration in your Startup class runs first; followed by the registration performed via IHostBuilder.ConfigureServices in the Program class.

If you switch the order of those two calls, you also switch the order of the registrations:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            services.AddHostedService<BackgroundServiceA>();
            services.AddHostedService<BackgroundServiceB>();
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

With this change, the registration performed via IHostBuilder.ConfigureServices runs first.

AddHostedService uses TryAddEnumerable behind the scenes. If TryAddEnumerable is called with a TService to TImplementation pair that already exists in the service collection, it's ignored. Consider the following example, slightly adapted from the code in your question:

services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceA>();

services.AddHostedService<BackgroundServiceB>();

The first call to add BackgroundServiceA succeeds, registering BackgroundServiceA against the IHostedService interface. The second time it's called, nothing happens, because this pair has already been registered.

The first call to add BackgroundServiceB also succeeds. Although this is using the same interface, IHostedService, it's a different implementation that's being registered, and so it's a different pair.

All of this means that the registration in your Startup.ConfigureServices is the one that will be used. This is because it runs first and because later registration ends up as a no-op.

like image 131
Kirk Larkin Avatar answered Oct 14 '25 04:10

Kirk Larkin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!