In my application I have some services to manage some devices on a PC (mainly via serial ports). Services will be accessed through API controllers.
Each service must run as singleton (single instance) as they will use a serial port each.
Currently I'm using TInterface
:TImplementationClass
pattern , to take advantage of DI/IoC framework, and to be able to write mock methods to test various functionalities.
I have a DeviceManagerService
that is the implementation class declared as follow:
DeviceManagerService : IDeviceManagerService
and IDeviceManagerService
is the service interface declared as follow:
IDeviceManagerService : IHostedService
I'm using IHostedService
interface to be able to do some startup configuration in StartAsync
method, as if I don't register it as HostedService, no method is launched at startup, but only the constructor is called when first accessing the service.
then in my API controller I have
SerialPortController(ILogger<SerialPortController> logger, IDeviceManagerService devicesManager)
so I'm able to call various methods of the service from the controller itself
I've tried registering my service like this:
builder.Services.AddSingleton<IDeviceManagerService, DeviceManagerService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<DeviceManagerService>());
but I'm getting
System.InvalidOperationException: 'No service for type 'APIServer.HostedServices.DeviceManagerService' has been registered.'
Also registering like this:
builder.Services.AddSingleton<DeviceManagerService>();
builder.Services.AddHostedService<IDeviceManagerService>(sp => sp.GetRequiredService<DeviceManagerService>());
throws an 'Unable to resolve service for type IDeviceManagerService' exception when calling IDeviceManagerService
from API controller.
Lastly I tried also doing this:
builder.Services.AddSingleton<IDeviceManagerService, DeviceManagerService>();
builder.Services.AddHostedService<DeviceManagerService>();
but two instances of DeviceManagerService
are created, and StartAsync
is called only once.
The only working method I found is this:
builder.Services.AddSingleton<DeviceManagerService>();
builder.Services.AddSingleton<IDeviceManagerService>(sp => sp.GetRequiredService<DeviceManagerService>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<DeviceManagerService>());
What is the purpose of AddHostedService if it doesn't register the Interface?
And why do I have to call AddSingleton separately once for the implementation class and once for the Interface?
Should this builder.Services.AddSingleton<IDeviceManagerService, DeviceManagerService>();
be sufficient to register the Interface and Implementation type and then using AddHostedService with delegate to create the instance?
What am I not getting here? I apologize for any misunderstanding as I'm fairly new to DI/IoC patterns. I'm migrating from legacy WCF to WebAPI/Grpc so I'm exploring various solutions here.
You were very close on your first attempt
builder.Services.AddSingleton<IDeviceManagerService, DeviceManagerService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<DeviceManagerService>());
Should be
builder.Services.AddSingleton<IDeviceManagerService, DeviceManagerService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<IDeviceManagerService>());
As you registered the DeviceManagerService
type as IDeviceManagerService
interface, you can only resolve the interface but not the specific type.