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:
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 yourIRootService
IRootService rootService = rootServiceFactory.CreateInstance(connectionString);
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 } }