I am trying to create a depenency injection pipeline service that runs as a Background service. The idea is that I can create one to many of these services and pass in simple IOption config for some values that need to change in each step (loaded using the service key), i regiter them a keyed services so they do not overwite each other.
I also have a queue mananger that is created as a singleton that allow me to get producers/consumers base on queue config also passed in a IOptions config.
I pass in a different service key for each version of the pipeline that i want to create
A simple version of my code (cut down to just the basics is below)
public IServiceCollection AddPipelineService(this IServiceCollection services,
IConfigurationManager configuration, string serviceKey)
{
// Get Config for the steps based on the sevice key
var StepOptions = Options.Create(
configuration
.GetSection($"{serviceKey}:{StepOptions.StepOptionsName}")
.Get<MessageFactoryOptions>());
// Build a list of steps and reister a service
services.AddKeyedSingleton<IStep, Step1>(serviceKey, (p,o) => new Step1(p.GetRequiredService<ILogger<ConfirmResponseMessageFactory>>(), stepOptions);
services.AddKeyedSingleton<IStep, Step2>(serviceKey, (p, o) => new Step2(p.GetRequiredService<ILogger<ConfirmResponseMessageFactory>>(), stepOptions);
services.AddKeyedSingleton<IStep, Step3>(serviceKey, (p, o) => new Step3(p.GetRequiredService<ILogger<ConfirmResponseMessageFactory>>(), stepOptions);
// Create the service provider
var provider = services.BuildServiceProvider(validateScopes: true);
// Add the steps to a set collection
IEnumerable<IStep> steps = provider.GetKeyedServices<IMessageProcessItem>(serviceKey);
services.AddKeyedSingleton<IStepCollection, StepCollection>(serviceKey, (p, o) => new StepCollection(steps));
// Create the queue options based on the service key
var queueOptions = Options.Create(
configuration
.GetSection($"{serviceKey}:{QueueOptions.QueueOptionsName}")
.Get<MessageFactoryOptions>());
// NOTE
// IQueueManager registered earlier as a singleton (i only want to onpen a siingle connection and reuse for creation of consumers and producers)
// PipelineBackgroundService is a BackgroundService
services.AddSingleton<IHostedService, PipelineBackgroundService>(
p => new PipelineBackgroundService(
p.GetRequiredService<ILogger<PipelineBackgroundService>>(),
p.GetRequiredService<IQueueManager>(),
p.GetRequiredKeyedService<IStepCollection>(serviceKey),
queueOptions);
return services;
}
All this works but i end up with multiple versions of queue manager becuase i call BuildServiceProvider which i believe creates a new container. But i have read i should avoid using BuildServiceProvider
Is there a way I can do this without this side affectand with out using BuildServiceProvider.
Thanks, Nick
Based on @eocron comment I was able to create the desired result with ot the need to call BuildServiceProvider, I just changed the code to use the provider in the registing of the StepCollection like so:
services.AddKeyedSingleton<IStepCollection, StepCollection>(serviceKey,
(p, o) => new StepCollection(p.GetKeyedServices<IMessageProcessItem>(serviceKey)));
and now there is only a single instance of the QueueManager created