Get type implementors using .NET microsoft dependency injection

99 Views Asked by At

I have i case where I need to get the type implementors registered in container to use them in BackgroundService.

public abstract class ServiceRunner
{
    public abstract Task RunAsync(CancellationToken token);
}

public class ServiceRunner<TService> : ServiceRunner
    where TService : IService
{

    ServiceRunner(/*ctor parameters*/)
    {
        ...
    }

    //class logic implementation

    public override async Task RunAsync(CancellationToken token)
    {
        ...
    }            
 }

So I'd like to get the ServiceRunner implementors, registered in container to run them all in BackgroundService worker.

public class Worker : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;

    public ScpWorker(IServiceProvider provider)
    {
        _serviceProvider = provider;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var services = _serviceProvider.GetServiceImplementors<ServiceRunner>(); //Have no idea how to implement this method

        var tasks = services.Select(s => s.RunAsync(stoppingToken));

        return Task.WhenAll(tasks);
    }
}

Any ideas on how I can accomplish this?

Thanks in advance for your help.

3

There are 3 best solutions below

2
Nima Airyana On

From my point of view, you can get all your objects in the container

var allServiceRunners = _serviceProvider.GetServices<YourType>();

so you can use them

1
Sachith Wickramaarachchi On

Try to implement your method something like this, it goes through all registered services and filters the implementations of the ServiceRunner<TService> and returns as a collection of ServiceRunner

public static IEnumerable<ServiceRunner> GetServiceImplementors<TService>(this IServiceProvider serviceProvider)
{
    var serviceType = typeof(TService);
    var serviceRunnerType = typeof(ServiceRunner<>).MakeGenericType(serviceType);
    var serviceRunners = serviceProvider.GetServices(serviceRunnerType);
    return serviceRunners.Cast<ServiceRunner>();
}
1
Balancing Ray On

In this case for my opinion you could register all specific generic classes just as ServiceRunner

// the place you register your dependencies
    services.AddSingleton<ServiceRunner, ServiceRunnerA>();
    services.AddSingleton<ServiceRunner, ServiceRunnerB>();

Or if you want to access to a specific ServiceRunner<T> in some logic:

// the place you register your dependencies
    services.AddSingleton<ServiceRunner<ServiceA>, ServiceRunnerA>();
    services.AddSingleton<ServiceRunner<ServiceB>, ServiceRunnerB>();
    services.AddSingleton<ServiceRunner>(s => s.Resolve<ServiceRunner<ServiceA>>());
    services.AddSingleton<ServiceRunner>(s => s.Resolve<ServiceRunner<ServiceB>>());

And then add IEnumerable<ServiceRunner> argument to the Worker constructor:

public class Worker : BackgroundService
{
    private readonly IEnumerable<ServiceRunner> _serviceRunners;

    public Worker(IEnumerable<ServiceRunner> serviceRunners)
    {
        _serviceRunners = serviceRunners;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        if(_serviceRunners == null || _serviceRunners.Count() == 0)
        {
            // log warning or throw an exception here
        }
        var tasks = _serviceRunners.Select(s => s.RunAsync(stoppingToken));

        return Task.WhenAll(tasks);
    }
}