Are there any issues calling Build() on IConfigurationBuilder outside of the startup class?

79 views Asked by At

We are trying to use Azure App Configuration in our project. We want the experience to be as smooth as possible for our developers. We would like to be able to only have a key in our local.settings.json file with the connection string and the client we are debugging. So something like this

{
  "IsEncrypted": false,
  "Values": {
    "AzureFunctionsJobHost__logging__logLevel__Evn": "Trace",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "AzureWebJobsStorage": "",
    "AppConfig": "Endpoint=***;Id=***",
    "Client": "clientId",
  }
}

This is a function application. We have implemented the following extension methods to allow the local.settings.json method to take precedence over Azure App Configuration if a developer wants to point just some of the values to a local database or storage account, something in the cloud, or something of that nature.

public static void AddAppConfiguration(this IConfigurationBuilder configurationBuilder,
    string client = null)
{
    if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("AppConfig")))
    {
        configurationBuilder.AddAzureAppConfiguration(options =>
        {
            options.Connect(Environment.GetEnvironmentVariable("AppConfig"))
                .Select(KeyFilter.Any, LabelFilter.Null)
                .Select(KeyFilter.Any, client ?? Environment.GetEnvironmentVariable("Client"));
        });

        configurationBuilder.PrioritizeAppSettings(); 
           configurationBuilder.SetEnvironmentVariableFromAzureIfNotExists("AzureWebJobsStorage");
    }
}
private static void PrioritizeAppSettings(this IConfigurationBuilder builder)
{
    // appsettings.json
    var appsettingsJson = builder.Sources.Where(x => x.GetType() == typeof(JsonConfigurationSource)).ToList();
    foreach (var source in appsettingsJson)
    {
        builder.Sources.Remove(source);
        builder.Sources.Add(source);
    }

    // local.settings.json
    var environmentJson = builder.Sources.Where(x => x.GetType() == typeof(EnvironmentVariablesConfigurationSource)).ToList();
    foreach (var source in environmentJson)
    {
        builder.Sources.Remove(source);
        builder.Sources.Add(source);
    }
}

(The second method was found here)

The method AddAppConfiguration() is called in our Startup.cs file like this:

class Startup : FunctionsStartup
{

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        builder.ConfigurationBuilder.AddAppConfiguration();
    }

    public override void Configure(IFunctionsHostBuilder builder)
    {     
        // add services      
    }
}

The issue we are facing is that the config value AzureWebJobsStorage is an empty string. We want to be able to get this value from Azure App Configuration if not passed from local.settings.json

I have implemented the following solution:

    private static void SetEnvironmentVariableFromAzureIfNotExists(this IConfigurationBuilder builder, string key)
    {
        if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(key)))
        {
            builder.Build().Providers
                .FirstOrDefault(p => p.GetType().Name == "AzureAppConfigurationProvider")
                .TryGet(key, out var constring);

            if (!string.IsNullOrWhiteSpace(constring))
                Environment.SetEnvironmentVariable(key, constring);
        }
    }

This is called in the AddAppConfiguration() after PrioritizeAppSettings()

The question I have and cant seem to find anything online is are there any issues with calling Build() on the builder interface outside of the startup class? Since under the hood .Net is do the something similar if not the same thing.

Just as a note this works locally and doesn't seem to cause any issues locally.

1

There are 1 answers

1
Kamohelo Vincent Ntimane On BEST ANSWER

Not sure if I got your question correct but here is what I have in mind:

Here are a few considerations:

  1. Timing: In a typical ASP.NET Core application, configuration is built and loaded during the application startup process. The Build() method is commonly called in the ConfigureServices method of the Startup class. This is where you configure and register services for your application. If you call Build() outside this context, you should ensure that it's done at the appropriate time and place in your application's lifecycle.

  2. Configuration Changes: Configuration settings can be loaded from various sources, and some sources may support live configuration updates (e.g., configuration providers that watch for changes). If you call Build() outside of the startup class and do not handle configuration updates appropriately, your application might not react to changes in configuration.

  3. Configuration Initialization: Calling Build() outside of the startup class may be necessary if you're using configuration in a context where the Startup class isn't readily accessible, such as in libraries or background services. However, make sure you do it in a way that doesn't lead to excessive configuration reloading and resource consumption.

  4. Testing: When writing unit tests, you might need to build a configuration for your tests. In such cases, it's common to call Build() on a configuration builder outside of the startup class.

  5. Thread Safety: Ensure that any calls to Build() are thread-safe if you're working in a multithreaded environment.

Ultimately, whether calling Build() outside of the startup class is a good practice or not depends on the specific requirements and context of your application. In many cases, it's perfectly acceptable to call Build() outside the startup class, but you should be aware of the considerations mentioned above and handle configuration appropriately based on your application's needs.