Reputation: 15101
I am learning Dependency Injection provided by .net core. So far, I have known 5 ways as follows.
// 0
sc.AddSingleton(configuration);
//1
sc.Configure<ServiceOption>(_ => configuration.GetSection(nameof(ServiceOption)).Bind(_));
//2
sc.Configure<ServiceOption>(configuration.GetSection(nameof(ServiceOption)));
//3
sc.AddOptions<ServiceOption>()
.Bind(configuration.GetSection(nameof(ServiceOption)));
//4
sc.Configure<ServiceOption>(_ => _.ID = "from hard code");
The last one (#4) is not so interesting to me, so let ignore it. I want to know, when do we need to use each of the remaining ones?
Here is the source code.
appsettings.json
file:
{
"ServiceOption": {
"ID": "from appsettings.json"
}
}
Code:
interface IService
{
void Do();
}
class Service : IService
{
readonly ServiceOption option;
readonly IConfigurationRoot configuration;
public Service(IOptionsMonitor<ServiceOption> option)
{
this.option = option.CurrentValue;
}
public Service(IConfigurationRoot configuration)
{
this.configuration = configuration;
}
public void Do(){}
}
class ServiceOption
{
public string ID { get; set; }
}
class Program
{
private static readonly IConfigurationRoot configuration;
static Program()
{
configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.Build();
}
static void Main()
{
using (ServiceProvider sp = RegisterServices())
{
sp.GetRequiredService<IService>().Do();
}
}
static ServiceProvider RegisterServices()
{
ServiceCollection sc = new ServiceCollection();
sc.AddTransient<IService, Service>();
int selector = 0;
switch (selector)
{
case 0:
sc.AddSingleton(configuration);
break;
case 1:
sc.Configure<ServiceOption>(_ => configuration.GetSection(nameof(ServiceOption)).Bind(_));
break;
case 2:
sc.Configure<ServiceOption>(configuration.GetSection(nameof(ServiceOption)));
break;
case 3:
sc.AddOptions<ServiceOption>()
.Bind(configuration.GetSection(nameof(ServiceOption)));
break;
default:
sc.Configure<ServiceOption>(_ => _.ID = "from hard code");
break;
}
return sc.BuildServiceProvider();
}
}
Upvotes: 0
Views: 279
Reputation: 247153
This answer is to add an additional option that was excluded
//bind to an object graph using `ConfigurationBinder.Get<T>`
ServiceOption setting = configuration.GetSection(nameof(ServiceOption)).Get<ServiceOption>();
//adding that to the service collection as needed
sc.AddSingleton(setting);
ConfigurationBinder.Get<T>
binds and returns the specified type.Get<T>
is more convenient than usingBind
.
In the above the option can be explicitly injected into dependents
public Service(ServiceOption option) {
this.option = option;
}
Its reasoning follows similar to an already provided answer if you do not want to couple your dependent code directly to framework related concerns.
Reference Configuration in ASP.NET Core: Bind to an object graph
Ideally you wouldn't want to be injecting IConfiguration
or IOptions
if you do not want your application code taking on unnecessary dependencies on framework abstractions.
Upvotes: 2
Reputation: 32068
sc.AddSingleton(configuration);
This registers a single instance of the class. This is useful when you don't want the consuming code to have to know about Microsoft.Extensions.Options
. Another common scenario is when you are not going to use the features provided by the Options pattern.
sc.Configure<ServiceOption>(_ => configuration.GetSection(nameof(ServiceOption)).Bind(_));
This allows you to use a function for the configuration of the options. The syntax you used, however, is quite weird since you are just binding to the already built Configuration.
sc.Configure<ServiceOption>(configuration.GetSection(nameof(ServiceOption)));
This does the same as above, but binds directly to the Configuration instance.
sc.AddOptions<ServiceOption>().Bind(configuration.GetSection(nameof(ServiceOption)));
This just gives you an OptionBuilder<T>
which you can use to call Bind
, Configure
and PostConfigure
in a single fluent call.
Upvotes: 2