-2

I'm trying to use already configured custom config-class to configure another service. Configuration gets data from local settings and Azure AppConfiguration store. This is my Startup code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAzureAppConfiguration();
    services.Configure<CustomConfig>(Configuration);

    services.AddTransient<ISomeService, SomeService>((serviceProvider) => 
        {
            CustomConfig config = serviceProvider.GetRequiredService<IOptions<CustomConfig>>().Value;
            return new SomeService(config);
        });
    //--------------------------------------------------------------------
    services.AddRazorPages();
    services.AddControllers();
}

But when SomeService is instantiated, my custom config-object doesn't contain data that should come from the Azure AppConfig. It has only data from the appsettings.json. What is wrong and what can I do here?

Svetlana
  • 163
  • 1
  • 15
  • My guess is that it's because you are binding to the **root** of the Configuration, rather than the appropriate *section*. You likely need `services.Configure(Configuration.GetSection("CustomConfig"))`. Where your configuration might be for example `{ "CustomConfig" : { “Prop1": "value" } }` or an azure appconfig key of `CustomConfig:Prop1`. But you've not shown an example of your configuration values so it's guess work at the moment (though very highly probable) – pinkfloydx33 Feb 01 '21 at 16:03
  • Not sure what is the fundamental difference between root and sections? My config has a hierarchical structure: root values are got from the appsettings.json and several structured sections values come from the Azure AppConfig. It works fine when I use config values in my controllers/pages but not in ConfigureServices(). Example of root value: "Env": "Dev", and AppConfig key: "Application:App:FrontDoorId" containing GUID. Configure gets data from all configured providers, but it seems that at the moment of SomeService object creation, only localsettings are bound. – Svetlana Feb 01 '21 at 21:31
  • The section name is Application:App and unless your CustomConfig class (which you've not shown) has a bunch of nested properties (one of them named Application which in turn has a property named App) then binding to the root is not going to work, which is how I know you are not binding to the correct thing. I use app settings, azure app config and various other sources and I can successfully get my config with the correct values in the same callback you are trying to, but only because I bind to the correct section – pinkfloydx33 Feb 01 '21 at 23:20
  • If your class has `public int A { get; set ;}` but your json looks like `{ B: { A: 1 } }` you have to bind to section named "B". Similarly if your AzureAppConfig has a value named `X:Y:A=1` then you have to bind to section "X:Y" – pinkfloydx33 Feb 01 '21 at 23:26

1 Answers1

0

So the short answer is: it's actually working. I was suspecting some stupid error, and it was exactly the case (a couple of code lines was commented so data was not retrieved from Azure - shame on me).

Thank you @pinkfloydx33 for the reassurance that the pattern should work.

And in case if someone wonders about binding root config values - it works as well. In my case, appsettings.json contains root values that I need to connect to Azure AppConfig store (primary and secondary endpoints, refresh interval and environment name which is used in key labels) and a few sections corresponding with external services: database, AAD B2C etc which are retrieved from Azure AppConfig. So my custom class has root values and a few nested classes like this:

public class CustomConfig : ConfigRoot
{
    // These root values come from appsettings.json or environment variables in Azure
    [IsRequired]
    public string Env { get; set; }

    [IsRequired, Regex("RegexAppConfigEndpoint")]
    public Uri AppConfigEndpointPrimary { get; set; }

    [IsRequired, Regex("RegexAppConfigEndpoint")]
    public Uri AppConfigEndpointSecondary { get; set; }

    public int AppConfigRefreshTimeoutMinutes { get; set; } = 30;

    // And these sections come from the Azure AppConfig(s) from the above
    public ConfigSectionDb Db { get; set; } = new ConfigSectionDb();

    public ConfigSectionB2c B2c { get; set; } = new ConfigSectionB2c();
    
    // Some other sections here...
}

Here ConfigSection<...> classes contain in their turn other subclasses. So I have quite a hierarchy here. ConfigRoot here is an abstract class providing Validate method.

And it works: this services.Configure<CustomConfig>(Configuration); part gets all the data - the root and the sections from all configured providers. In my case, it's two Azure AppConfigs, appsettings.json, environment variables.

Svetlana
  • 163
  • 1
  • 15