AppSettings with new Program.cs/IHostingStartup

For sensitive information such as ConnectionStrings and passwords, I have used to keep this in separate configuration files, that I can keep out of version control.

With previous versions of ServiceStack I’d just do like this, in Program.cs:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext,appConfig) =>
            {
                IHostEnvironment env = hostingContext.HostingEnvironment;       // dev, prod, etc.

                appConfig.Sources.Clear();
                appConfig.SetBasePath(System.AppDomain.CurrentDomain.BaseDirectory)
                    .AddJsonFile("appsettings.json", optional: false)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) 
                    .AddJsonFile("appsettings_passwords.json", optional: true)           

Then in Configure.Db.cs:

[Priority(-1)]      // run before AppHost's Startup()
public class ConfigureDb : IConfigureServices, IConfigureAppHost
{
    IConfiguration Configuration { get; }
    public ConfigureDb(IConfiguration configuration) => Configuration = configuration;
        // ...
        var connectionString = Configuration.GetConnectionString("DbConnectionString") ?? ":memory:";

Now with the new Program.cs format (the one without class and namespace, created by x new empty), I tried to add in a file here, only to discover that Program.cs is run after Configure.Db.cs (added with x mix sqlite).

How can I achieve the same result (keeping passords etc. in a separate file) with the new style (HostingStartup, Program.cs etc)?

That’s not possible given Program.cs is the entry point of the Application.

Note the Hosting Startup used in new Modular Startup is ASP .NET Core’s (i.e. not ServiceStack), so you shouldn’t have any issues with execution order.

In ASP .NET Core 6 you would configure your application configuration in Program.cs, e.g:

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json");
//...

Then your ConfigureDb isn’t right, if you mix in a RDBMS you should get ConfigureDb implementing IHostingStartup where you’ll be able to access configuration when registering its dependencies:

public class ConfigureDb : IHostingStartup
{
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices((context, services) => {
            services.AddSingleton<IDbConnectionFactory>(new OrmLiteConnectionFactory(
                context.Configuration.GetConnectionString("DefaultConnection")
                ?? ":memory:",
                SqliteDialect.Provider));
        })
//..

Yes, if I put a breakpoint on line 1 in Program.cs, it stops there, before any Configuration.Db.cs is called.

var builder = WebApplication.CreateBuilder(args);

But that line is the one which calls Configuration.Db.cs somehow, because the next line is not run until after Configure.Db.cs has been called, thus too late.

builder.Configuration.AddJsonFile("appsettings.json"); // called after Configure.Db.cs' ConfigureServices()

ok yeah, in that case it doesn’t look like you can use the default builder, the ASP.NET Core HostingStartup docs are showing you can use Host.CreateDefaultBuilder(args) to create a customized builder, e.g:

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(builder => 
        builder.ConfigureAppConfiguration(configure => {
            configure.AddJsonFile("appsettings.json");
        }));

Otherwise you should be able to use a IHostingStartup to configure builder.ConfigureAppConfiguration(), although I’m not sure how you would enforce the order they’re executed when they’re in the same assembly as the docs only mention they’re executed in the order assemblies are listed when they’re are multiple assemblies.

1 Like

But that returns a different class. Instead of WebApplicationBuilder, which works with e.g. app.UseServiceStack() and such, we get IHostBuilder. So then the whole Program.cs needs to be rewritten to “old style ServiceStack” or?

We’re mesmerized that the IHostingStartup is run at CreateBuilder() and not at Build(). What was MS thinking…

Not sure how you’d change configuration before their hosting startup assemblies are called, the only configuration they’ll let you change at construction is basic host configuration:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    ApplicationName = typeof(Program).Assembly.FullName,
    ContentRootPath = Directory.GetCurrentDirectory(),
    EnvironmentName = Environments.Staging,
    WebRootPath = "customwwwroot"
});

If they don’t provide an opportunity to configure configuration before IHostingStartup, only solutions I can think of is creating a hosting startup that customizes configuration, hopefully they’ll be ordered in alphabetical order so you can customize it before others are run, otherwise you’d need to move the service registration out of Configure.Db.cs into builder.Services....

Otherwise try posting an issue on https://github.com/dotnet/aspnetcore asking the best solution, they’re usually pretty responsive.

We’ll try at StackOverflow, maybe someone else have a similar problem? I miss the old ModularStartup.


1 Like