Home > Mobile >  Detect that UseStartup<TStartup> was called in ASP.NET Core
Detect that UseStartup<TStartup> was called in ASP.NET Core

Time:04-08

When configuring ASP.NET Core startup there's this:

.ConfigureWebHostDefaults(webHostBuilder => {
  //add services here or in Startup.ConfigureServices()   // (1)
  webHostBuilder.UseStartup<Startup>();
})
.ConfigureServices(serviceCollection => {
  //add services here                                     // (2a)
})
.UseMyServices()                                          // (2b)

I must register something after the web host starts. I could do so at location (2a), or in a helper UseMyServices() extension method (2b), which is effectively the same thing.

public static IHostBuilder UseMyServices(this IHostBuilder hostBuilder)
{
  hostBuilder.ConfigureServices((hostBuilderContext, serviceCollection) => {
    //add services here
  };
}

Inside UseServices() I want to do a sanity check to ensure the code was placed at the correct location. Can I use hostBuilderContext or serviceCollection to detect that the UseStartup<T>() call was already made?

I looked in the serviceCollection to find something interesting, e.g. IStartup, but didn't find anything useful. Maybe I'm not looking for the right thing.

Summary: how do I detect that the UseStartup<TStartup> call has been made?

CodePudding user response:

All services will be registered before Configure is called in Startup, regardless of whether the hook for registration is before or after UseWebHostDefault. If this is your concern, there's nothing that can be done. Note that the order that calls to ConfigureServices are made will be respected.

I've not really found any "good" solutions, but I did notice that the code for ConfigureWebHost (and therefore ConfigureWebHostDefaults) registers GenericWebHostService as an IHostedService, so maybe you could use this.

The issue here is that the service is internal and it's an implementation detail, so there's no guarantee that this won't be changed/removed/renamed in a future version. Anyway, if you are OK with that risk, then you could write the following code in your extension method:

public static IHostBuilder UseMyServices(this IHostBuilder hostBuilder)
{
    return hostBuilder
        .ConfigureServices(svc =>
        {
            if (svc.Any(svc => svc.ServiceType == typeof(IHostedService) && svc.ImplementationType.Name == "GenericWebHostService"))
            {
                System.Diagnostics.Debug.WriteLine("After UseWebHostDefault");
            }
            // do your other service registrations
        });
}

The problem here is that this doesn't specifically detect that UseStartup was called, just that ConfigureWebHostDefaults was run.

--

Another thing I've found is that UseStartup adds a property to the HostBuilderContext properties (code). Unfortunately the key it uses is a private instance field in an internal class (see here). Even if you wanted to take the risk of implementation changes causing runtime errors here, I'm not sure how you would access the key from this class.

If you just want to check if Startup in your project has been called, you could write something like this to check if its instance has been registered:

public static IHostBuilder UseMyServices(this IHostBuilder hostBuilder)
{
    return hostBuilder
        .ConfigureServices((ctx, svc) =>
        {
            if (ctx.Properties.Any(p => p.Value?.GetType() == typeof(Startup)))
            {
                System.Diagnostics.Debug.WriteLine("After Startup");
            }
            // do your other service registrations
        });
}
  • Related