Accessing top-level fields in AppSettings.json from injected configuration class

3.8k Views Asked by At

Say we have such AppSettings.json

{
  "Region": Europe,
  "WeirdService": {
    "JustField": "value"
  }
}

Registering WeirdService settings in separate, singleton class (or using options pattern) is fine, just:

service.AddSingleton(configuration.GetSection("WeirdService").Get<WeirdService>();

And at this point it's fine. I don't know however how to deal cleanly with this top-level properties like Region in my example.

I know I can just inject IConfiguration and use config.GetValue<string>("Region") or just access configuration directly, but I wonder if there is some clean, better way without hardcoding this stuff in services.

Edit

I forgot to mention. Team I'm currently working with uses .NET Core 3.1 as it's current LTS release.

5

There are 5 best solutions below

1
Plevi On BEST ANSWER

I think the easiest way would be to just create a class for the toplevel keys. In your case you could create something like AppConfig with the single property Region. Then you just register it without getting a config section using the Configuration object, the Configure methods asks for a Configuration interface anyway and not a ConfigurationSection.

AppConfig:

public class AppConfig
{
    public string? Region { get; set; }
}

Registration:

public static IServiceCollection AddOptions(this IServiceCollection services, IConfiguration configuration)
{
    return services.Configure<AppConfig>(configuration);
}

Usage:

public class ExampleConsumer
{
    public ExampleConsumer(IOptions<AppConfig> appConfig) {}
}
2
Harkirat singh On

You can bind section to a class as well, it allows clean usage without using much of the magic strings.

public class WeirdService{
 public string JustField{ get; set;}  
 public string AnotherField{ get; set;}
 }

In controller you can then define a field

private readonly WeirdService _weirdService = new WeirdService();
 public UserController(IConfiguration configuration)
{
    configuration.GetSection("WeirdService").Bind(_weirdService);
    
    //_weirdService.JustField
    //_weirdService.AnotherField
}

You can access Region field using

configuration.GetValue<string>("Region")

or other way is

public Startup(IConfiguration configuration, IWebHostEnvironment 
 webHostEnvironment)
    {
        Configuration = configuration;
        environment = webHostEnvironment;
    }

then you can just use

Configuration["Region"]

5
Safyan Yaqoob On

Updated

Method 1 Preferred way

The preferred way to read related configuration values is using the options pattern.

For more detail about the options pattern check the link below.

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0

 "WeirdService": {
     "JustField": "value"
  }

Create the WeiredServiceOptions class.

public class WeiredServiceOptions
{
   public const string WeiredService = "WeiredService";
   public string JustField { get; set; }
}

An options class:

  1. Must be non-abstract with a public parameterless constructor.
  2. All public read-write properties of the type are bound.
  3. Fields are not bound. In the preceding code, WeiredService is not bound. The Position property is used so the string WeiredService doesn't need to be hardcoded in the app when binding the class to a configuration provider.

Calls ConfigurationBinder.Bind to bind the WeiredServiceOptions class to the WeiredService section.

var weiredServiceOptions = new PositionOptions();
configuration.GetSection(PositionOptions.Position).Bind(positionOptions);

An alternative approach when using the options pattern is to bind the WeiredService section and add it to the dependency injection service container. In the following code, WeiredServiceOptions is added to the service container with Configure and bound to the configuration

services.Configure<WeiredServiceOptions>(Configuration.GetSection(
                                    WeiredServiceOptions.Position));

and then read the WeiredService Options.

private readonly WeiredServiceOptions _options;

public YourClassContructor(IOptions<WeiredServiceOptions> options)
{
   _options = options
}

Console.WriteLine($"JustField: {_options.JustField}");

Method 2

service.AddSingleton(configuration.GetSection("WeirdService:JustField").value);

Method 3

service.AddSingleton(configuration["WeirdService:JustField"]);
1
galdin On

You got two options

Don't have any top level fields

All top level fields would go one level in. Your configuration would look something like:

{
  "App": {
    "Region": "east-us-2",
    "ShowMaintenancePrompt": false
  },
  // other options follow
}

The advantage of this approach is you can keep adding to "App" as your application grows, and continue to use the options pattern.

Gather top-level fields into a class, and register that with DI

For a configuration like:

{
  "Region": "east-us-2"
}

Create a AppConfig class like:

internal class AppConfig
{
    public string? Region { get; set; }
}

And register this class with the DI:

var toplevelConfig = new AppConfig {
    Region = configuration.GetValue<string>("Region")
};
services.AddSingleton<AppConfig>(toplevelConfig);

You can now inject AppConfig anywhere you'd like.

The only minor downside to this is that you cannot use the options pattern anymore.

Avoid injecting IConfiguration directly.

0
Praveen Prabha Ravindran On

Doing: services.AddSingleton(Configuration.GetSection("WeirdService").Get<WeirdService>()); will register WeirdService to the Ioc container without supporting Options pattern. Assuming this is what you are looking for. Here is what you could do:

  1. Create a class with properties mathcing the top level configuration similar to the AppConfig class a couple of people have suggested in the answers
  2. Register the AppConfig class with the Ioc as below:
    services.AddSingleton(Configuration.Get<AppConfig>());
    

Note:

  • Doing Configuration.Get<AppConfig>() will bind matching properties on the AppConfig class with the corresponding values from appsettings.json
    • Feel free to skip properties for keys that you do not want to bind
  • The IConfiguration.Get<T> is an extension method defined in Microsoft.Extensions.Configuration.ConfigurationBinder just in case