Is there a better way to link related dependencies using a DI Container?

80 Views Asked by At

I need to get all connected SerialPorts and poll informations from them. So I wrote a Helper to get the connected SerialPorts, loop them in the Startup.ConfigureService method and add them to the DI Container:

foreach (var serialPort in SystemInformationHelper.GetSerialPorts())
{
   services.AddSingleton<IAddress>(sp => new SerialAddress(serialPort));
   services.AddSingleton<IConnection, SerialConnection>();
   services.AddSingleton<IDevice, PollingDevice>();
}

Then I used constructor injection to get all Connections in my PollingDevice and all Addresses in my Connection Class. See Class Diagram:

enter image description here

Now I gave the Startup Method that is needed to start the Polling process an index to get the correct injected connection and added a HostedService to the Project that is used to control the connected devices.

public class ControllerInitializer : IHostedService
{
   private readonly IEnumerable<IDevice> _devices;

   public ControllerInitializer(IEnumerable<IDevice> devices)
   {
      _devices = devices;
   }

   public Task StartAsync(CancellationToken cancellationToken)
   {
      for(int i = 0; i < _devices.Count(); i++)
      {
        _devices.ElementAt(i).Startup(i);
      }
      return Task.CompletedTask;
   }

   public Task StopAsync(CancellationToken cancellationToken)
   {
       for (int i = 0; i < _devices.Count(); i++)
       {
          _devices.ElementAt(i).Shutdown();
       }
       return Task.CompletedTask;
   }
}

It works, but I just started using DI and wanted to know if there is any better solution I don't know about.

1

There are 1 best solutions below

0
Lolop On BEST ANSWER

Injecting an IEnumerable<T> of dependencies is a pretty common thing with DI, so that seems perfectly reasonable to me.

For example Microsoft uses the same pattern to inject retrieve all the IHostedService's to call StartAsync/StopAsync. Only difference is they directly retrieve the dependency from the IServiceProvider instead of injecting it into a constructor.

Only thing I'd change is to use a foreach loop instead of a for loop with the IEnumerable:

public Task StartAsync(CancellationToken cancellationToken)
{ 
    var connectionIndex = 0;
    foreach (var device in _devices) 
    {
        device.Startup(connectionIndex);
        connectionIndex++;
    }

    return Task.CompletedTask;
}