Issue understanding a behaviour linked to appsettings binding in .NET Core

Viewed 153

I'm facing a weird issue where I can't seem to bind appsettings values from my appsettings.Development.json file using Configuration.Get<T>(). I've done it countless times on different project in the exact same way but apparently "not this time".

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        var myConfig = Configuration.Get<MySettingType>();
    }
}

Usually this works perfectly. I left the Program.cs clean from any edit that could change the default behaviour regarding loading the appsettings files. MySettingType is the same name as the key in the appsettings file, matching class is using properties, all public. The configuration is at the root of the json file as well.

What happened is that the code would only lead to null values being filled in the myConfig object. Now I solved the issue using this code instead of Configuration.Get<T>():

var myConfig = new MySettingType();
Configuration.GetSection(nameof(MySettingType)).Bind(myConfig);

This is all cool, but the question is why did I have to use that? What could explain it?

2 Answers

I would always include a GetSection. Something like this:
Configuration.GetSection(nameof(MySettingType)).Get<MySettingType>();

But do you really need the myConfig later in your ConfigureServices method?
If not, you can also do this:
services.Configure<MySettingType>(Configuration.GetSection(nameof(MySettingType));


You can find different examples of how Microsoft suggest to bind hierarchical configuration.

When binding options, it is assumed that the structure of T will be the same as whatever the Configuration is. It does not take the type name into account. So if you want to bind the options from a specific part of the configuration keyed by your type, you'll need to use GetSection which will return a new ConfigurationSection object scoped to that section. Then the same rules apply since.

If you want a convention, you could write an extension method that could bind based on type names for you.

public static T BindByTypeName<T>(this IConfiguration configuration)
    where T : class, new()
{
    return configuration.Bind<T>(typeof(T).Name);
}

You could also wrap MySettingsType with another AppSettings or something so that your options match your appsettings.json structure.

class AppSettings
{
    public MySettingsType MySettingsType { get; set; }
}

Then you can simply call Get<AppSettings> on the root configuration.

Configuration.Get<AppSettings>().MySettingsType;
Related