How can I pass a runtime parameter as part of the dependency resolution?

Simple configuration

public void ConfigureServices(IServiceCollection services)
{
    // Choose Scope, Singleton or Transient method
    services.AddSingleton<IRootService, RootService>();
    services.AddSingleton<INestedService, NestedService>(serviceProvider=>
    {
         return new NestedService("someConnectionString");
    });
}

With appSettings.json

If you decide to hide your connection string inside appSettings.json, e.g:

"Data": {
  "ConnectionString": "someConnectionString"
}

Then provided that you've loaded your appSettings.json in the ConfigurationBuilder (usually located in the constructor of the Startup class), then your ConfigureServices would look like this:

public void ConfigureServices(IServiceCollection services)
{
    // Choose Scope, Singleton or Transient method
    services.AddSingleton<IRootService, RootService>();
    services.AddSingleton<INestedService, NestedService>(serviceProvider=>
    {
         var connectionString = Configuration["Data:ConnectionString"];
         return new NestedService(connectionString);
    });
}

With extension methods

namespace Microsoft.Extensions.DependencyInjection
{
    public static class RootServiceExtensions //you can pick a better name
    {
        //again pick a better name
        public static IServiceCollection AddRootServices(this IServiceCollection services, string connectionString) 
        {
            // Choose Scope, Singleton or Transient method
            services.AddSingleton<IRootService, RootService>();
            services.AddSingleton<INestedService, NestedService>(_ => 
              new NestedService(connectionString));
        }
    }
}

Then your ConfigureServices method would look like this

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration["Data:ConnectionString"];
    services.AddRootServices(connectionString);
}

With options builder

Should you need more parameters, you can go a step further and create an options class which you pass to RootService's constructor. If it becomes complex, you can use the Builder pattern.


To pass a runtime parameter not known at the start of the application, you have to use the factory pattern. You have two options here:

  1. factory class (similar to how IHttpClientFactory is implemented)

     public class RootService : IRootService
     {
         public RootService(INestedService nested, IOtherService other)
         {
             // ...
         }
     }
    
     public class RootServiceFactory : IRootServiceFactory 
     {
         // in case you need other dependencies, that can be resolved by DI
         private readonly IServiceProvider services;
    
         public RootServiceFactory(IServiceProvider services)
         {
             this.services = services;
         }
    
         public IRootService CreateInstance(string connectionString) 
         {
             // instantiate service that needs runtime parameter
             var nestedService = new NestedService(connectionString);
    
             // note that in this example, RootService also has a dependency on
             // IOtherService - ActivatorUtilities.CreateInstance will automagically
             // resolve that dependency, and any others not explicitly provided, from
             // the specified IServiceProvider
             return ActivatorUtilities.CreateInstance<RootService>(services,
                 new object[] { nestedService, });
         }
     }
    

    and inject IRootServiceFactory instead of your IRootService

     IRootService rootService = rootServiceFactory.CreateInstance(connectionString);
    
  2. factory method

     services.AddTransient<Func<string,INestedService>>((provider) => 
     {
         return new Func<string,INestedService>( 
             (connectionString) => new NestedService(connectionString)
         );
     });
    

    and inject the factory method into your service instead of INestedService

     public class RootService : IRootService
     {
         public INestedService NestedService { get; set; }
    
         public RootService(Func<string,INestedService> nestedServiceFactory)
         {
             NestedService = nestedServiceFactory("ConnectionStringHere");
         }
    
         public void DoSomething()
         {
             // implement
         }
     }
    

    or resolve it per call

     public class RootService : IRootService
     {
         public Func<string,INestedService> NestedServiceFactory { get; set; }
    
         public RootService(Func<string,INestedService> nestedServiceFactory)
         {
             NestedServiceFactory = nestedServiceFactory;
         }
    
         public void DoSomething(string connectionString)
         {
             var nestedService = nestedServiceFactory(connectionString);
    
             // implement
         }
     }