Reputation: 2916
I have this service that will be injected to some other controllers.
It needs a service, and a connection string that is taken from a configuration file.
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, string connectionString){
//...
}
}
I would like IService1
and IService2
to be injected, but connectionString
to be specified manually. I can't get my head around a way to work this out, the examples I saw were either massively complex, or just not what I wanted to achieve.
public void ConfigureServices(IServiceCollection services)
{
var cfg = new MyConfiguration();
Configuration.Bind("config", cfg);
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService, MyService>(/*what can I do here?*/)
}
Can this be achieved simply ?
Upvotes: 4
Views: 3023
Reputation: 247283
You have access to the service provider within the factory delegate.
Resolve the other dependencies and inject the static variable when initializing the service.
//...
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService>(_ =>
new MyService(_.GetService<IService1>(), _.GetService<IService2>(), connectionString));
In the above example _
in the factory delegate is a IServiceProvider
, and GetService<T>
extension method is used to resolve the other services provided they are also registered with the service collection.
The factory delegate will be invoked the first time IMyService
is requested.
As an alternative, referencing Options pattern in ASP.NET Core
And assuming for example a settings.json file
{
"myConnectionString": "value1_from_json",
}
consider using the IOptions<T>
provided by the configuration extension.
create a class to store the desired configuration.
public class MyConnections {
public string MyConnectionString { get; set; }
}
refactor the class to depend on IOption<MyConnections>
public class MyService : IMyService {
private string connectionString;
public MyService(IService1 service1, IService2 service2, IOptions<MyConnections> options){
connectionString = options.Value.MyConnectionString;
//...
}
//...
}
and configure it on start up
//...
services.Configure<MyConnections>(Configuration);
services.AddSingleton<IMyService, MyService>();
Upvotes: 3
Reputation: 387915
You could do it like Nkosi suggests and use a factory that creates the service. This however has the drawback that this is very explicit and will require you to always adjust the object creation whenever your constructor signature changes (for example when you need different dependencies).
A more proper solution in the ASP.NET Core world would be to use the options pattern. What you do is basically instead of requiring a connection string to be passed to the constructor, you create a new configuration type that configures your service. You can then configure this object using the options pattern.
This would look like this:
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, IOptions<MyServiceOptions> serviceOptions)
{
var connectionString = serviceOptions.Value.ConnectionString;
//...
}
}
public class MyServiceOptions
{
public string ConnectionString
{ get; set; }
}
And then in your startup, just register your type and configure the options:
services.AddSingleton<IMyService, MyService>();
services.Configure<MyServiceOptions>(options => {
options.ConnectionString = "connection string";
});
If you place the configuration for your connection string into a separate configuration section, you could even do this:
services.Configure<MyServiceOption>(configuration.GetSection("MyService"));
Assuming your configuration looks like this:
{
"MyService": {
"ConnectionString": "…"
},
// …
}
Upvotes: 2