Search code examples
c#reflection.net-6.0worker-service

.NET 6 Is it possible to scan the background services and invoke AddHostedService<T> dynamically without adding them one by one


I am now using .NET 6 Worker Service to develop an backend service, and I have multi background services need to be registered by AddHostedService<> like:

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<WorkerA>();
        services.AddHostedService<WorkerB>();
        services.AddHostedService<WorkerC>();
    })
    .Build();

await host.RunAsync();

I want to find a better way to register background services dynamically, for example:

var workers = Assembly.GetExecutingAssembly().GetTypes()
    .Where(worker =>
        worker is {IsClass: true, IsAbstract: false} &&
        !string.IsNullOrWhiteSpace(worker.Namespace) &&
        worker.Namespace.Equals(typeof(BaseWorker).Namespace))
    .ToArray();

foreach (var worker in workers)
{
    // Incorrect here
    services.AddHostedService<worker>();
}

However I failed since the worker is a Type not a class name. Could someone give me some instructions, thank you.


Solution

  • It is possible. You need to make a generic method for each type and call it. First get the open generic method:

    var method = 
        // for the static extensions class where AddHostedService is defined
        typeof(ServiceCollectionHostedServiceExtensions)
        // get the AddHostedService<>() method
        .GetMethod(
            "AddHostedService",
            // that has one generic argument
            1,
            // that is static and public
            BindingFlags.Static | BindingFlags.Public,
            // using the default binder
            null,
            // that takes one IServiceCollection as a parameter
            new Type[] { typeof(IServiceCollection },
            null));
    

    Then change your foreach such that it creates a closed generic version of the method with MakeGenericMethod, and invokes it.

    foreach (var worker in workers)
    {
        // if worker is a MyWorker, then the below code is
        // equivalent to services.AddHostedService<MyWorker>()
    
        method
            .MakeGenericMethod(new Type[] { worker })
            .Invoke(null, new object?[] { services });
    }
    

    This allows you to call the ServiceCollectionHostedServiceExtensions.AddHostedService method via reflection.