ServiceCollection IOptions<> won't bind properly

1.3k Views Asked by At

I am trying to bind some configurable settings. The values are provided via the app-settings.local.json. The Model to bind to is called Configurable Settings. I have attempted to follow a few tutorials and troubleshoot the issue:

  1. https://andrewlock.net/how-to-use-the-ioptions-pattern-for-configuration-in-asp-net-core-rc2/
  2. https://github.com/aspnet/Configuration/issues/411
  3. Cannot set Configuration from JSON appsettings file in .NET Core project
  4. ServiceCollection returns null for IOptions even though GetSection is working

I have attempted to follow the advice given here and apply it in my own application. I could not get it to work after 4 hours of trying. Either I lack the basic knowledge needed to implement this or I'm overlooking something.

My ConfigurableSettings class is structured as follows:

    public class ConfigurableSettings
    {
        public AppSettings _AppSettings;
        public DgeSettings _DgeSettings;
        
        public class AppSettings
        {
            [JsonProperty("SoftDelete")] 
            public bool SoftDelete { get; set; }
        }

        public class DgeSettings
        {
            [JsonProperty("zipFileUrl")] 
            public string zipFileUrl { get; set; }

            [JsonProperty("sourceFileUrl")] 
            public string sourceFileUrl { get; set; }
        }
    }

My ConfigureServices is structured as follows:

    public static void ConfigureServices(IServiceCollection serviceCollection, string[] args)
        {
            var serviceCollection = new ServiceCollection(); 
            
            serviceCollection.AddOptions();
            
            var configuration = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
                                .AddJsonFile("app-settings.local.json", true)
                                .AddJsonFile("app-settings.json", false)
                                .Build();

            serviceCollection.Configure<ConfigurableSettings>(options => configuration.GetSection("AppSettings").Bind(options));
            serviceCollection.Configure<ConfigurableSettings>(options => configuration.GetSection("DgeSettings").Bind(options));

            var services = serviceCollection.BuildServiceProvider();
            var options = services.GetService<IOptions<ConfigurableSettings>>();

            serviceCollection.AddLogging(loggingBuilder =>
            {
                loggingBuilder.AddConfiguration(configuration.GetSection("Logging"));
                loggingBuilder.AddConsole();
                loggingBuilder.AddDebug();
            });

            serviceCollection.AddServices(configuration);
            serviceCollection.AddNopCommerceServices(configuration);

            serviceCollection.AddTransient<Comparator>();
            serviceCollection.AddTransient<UpdateManager>();
            serviceCollection.AddTransient<DgeRequestAuthenticator>();
            serviceCollection.AddTransient<ISourceConnector, DgeConnector>();
            serviceCollection.AddTransient<IDestinationConnector, NopCommerceConnector>();
        }

My app-settings.local.json is configured as follows:

{
  "AppSettings": {
    "SoftDelete": true
  },

  "DgeSettings": {
    "zipFileUrl" : "www.example-url.com",
    "sourceFileUrl" : "www.example-url.com"
  }
}

When I attempt to use it in a class, I call it in my constructor as follows:

private readonly ConfigurableSettings _settings;
        
        public AlphaBetaService(IOptions<ConfigurableSettings> settings)
        {
            _settings = settings.Value;
        }
    

Could someone help me to find out what I am doing wrong?

1

There are 1 best solutions below

2
On

Not sure how do you use ConfigureServices method in your project, actually the ConfigureServices in Startup.cs must either be parameterless or take only one parameter of type IServiceCollection.

Change your ConfigureServices like below:

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

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var configuration = new ConfigurationBuilder()
                            .SetBasePath(Directory.GetCurrentDirectory())
                            .AddJsonFile("app-settings.local.json", true)
                            .Build();
        services.Configure<ConfigurableSettings>(configuration);

        //...
    }
}

Also change your model design like below:

public class ConfigurableSettings
{
    public AppSettings AppSettings{ get; set; }
    public DgeSettings DgeSettings { get; set; }        
}
public class AppSettings
{
    [JsonProperty("SoftDelete")]
    public bool SoftDelete { get; set; }
}

public class DgeSettings
{
    [JsonProperty("zipFileUrl")]
    public string zipFileUrl { get; set; }

    [JsonProperty("sourceFileUrl")]
    public string sourceFileUrl { get; set; }
}