Search code examples
c#dependency-injectiondryioc

DryIoc: Registering a collection of dependencies of the same implementation with different constructor parameters


I have a service, let's call it SearchService that is using several endpoints, each of them handling a specific language. The endpoints are represented by Client classes that are initialized based on the contents of a configuration file. I'd like to inject all the clients into the main service as a dependency, but the number of clients is can be determined only at runtime. Here is a simplified example:

public class SearchService : ISearchService
{
   public SearchService(IReadOnlyList[] clients) => this.clients = clients;
   public string Search(Language language) { // call language-specific client }
}
    
public class Client : IClient
{
   public Client(Language language) => Language = language
   public Language Language { get; }
   public string Search() { // call language-specific endpoint }
}

At the composition root, I'd like to set up the dependencies for the service. I'm using DryIoc and what I've so far is:

container.Register<ISearchService, SearchService>();
foreach (var config in SearchClientConfigurations)
{
    var client = new Client(config.Language);
    container.RegisterInstance<IClient>(client);
}

Which seems to be fine, if I call container.Resolve<ISearchService>() as a test, the service receives the list of clients as intended. However, this results the clients being created as singletons (up to my best knowledge) and even if the service and the clients would have a singleton lifetime, it just doesn't feel right. I'd like the clients to have the same lifetime as the service e.g. by using Setup.UseParentReuse. Adding this to the client registration doesn't work of course (it completely makes sense).

I've went through the documentation several times and did a lot of searching; probably I'm just too confused now to find the solution.

I was thinking injecting a factory into the service that would create the clients, then the factory could have the same lifetime as the service, but my gut feeling tells me that it's not the best way to do it: handling the lifetime of the clients should be the responsibility of the DI container. I guess I need to specify a factory method (I've already read the relevant chapter in the documentation) but I just can't figure it out.


Solution

  • In DryIoc stick to the simplest straightforward registration, but just avoid RegisterDelegate<T>((IResolverContext r) => ...) as it is a black box service locator, use the dependency typed delegates instead:

    foreach (var config in SearchClientConfigurations)
    { 
        var lang = config.Language;
        container.RegisterDelegate<IClient>(() => new Client(lang), 
            setup: Setup.With(useParentReuse: true)); // or whatever other reuse or setup
    }