NetCoreAppSettings returns null object

I’m not sure what’s happened, but anytime I call AppSettings.Get<EmailConfig>("Email"), the object returned is null. This is in my main AppHostBase project (React).

I’m using the new NetCoreAppSettings(Configuration).

The strange thing is it works in my other root project (a standalone host that runs in a non-http environment, BasicAppHost, strictly for MQ services).

Setup of Non-working project

appsettings.json

{
    "ConnectionStrings": {
        "MainDatabase": "....."
    },
    "servicestack": {
        "license": "...."
    },
    "Environment":  "Local", 
    "Logging": {
        "IncludeScopes": false,
        "Debug": {
                "LogLevel": {
                "Default": "Warning"
            }
        },
        "Console": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    },
    "Email": {
        "From": "noreply@myapp.com",
        "ApiKey":  "EMPTY"
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

    Configuration = builder.Build();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions()
        {
            HotModuleReplacement = true,
            ReactHotModuleReplacement = true
        });
    }

    app.UseServiceStack(new AppHost()
    {
        AppSettings = new MultiAppSettings(
                new NetCoreAppSettings(Configuration)
            )
    });
}

public class AppHost : AppHostBase
{
    public AppHost() : base("MyApp", typeof(PacketService).Assembly) { }

    // Configure your AppHost with the necessary configuration and dependencies your App needs
    public override void Configure(Container container)
    {
        SetConfig(new HostConfig
        {
            AddRedirectParamsToQueryString = true,
        });
        
        container.Register<IPasswordHasher<User>>(
            new PasswordHasher<User>(new OptionsWrapper<PasswordHasherOptions>(new PasswordHasherOptions()
            {
                CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2
            })));
        
        container.Register<IPasswordHasher>(new CustomPasswordHasher(container.Resolve<IPasswordHasher<User>>()));

        var emailSEttings = AppSettings.Get<EmailConfig>("Email"); // This is null
        
        this.AddDatabase();

        this.AddPlugins();

        this.AddEmail();

        this.AddProtection();
        
        JsConfig.DateHandler = DateHandler.ISO8601;
    }
}

EmailConfig.cs

public class EmailConfig
{
    public string From { get; set; }
    public string ApiKey { get; set; }
}

When I Debug AppSettings in AppHost.Configure, I see the NetCoreAppSettings, which lets me dig down into the values and Email:From, Email:ApiKey are in fact there.

Have I set something up wrong?

I’m using ServiceStack version 5.0.2

Do you know how you would populate this in .NET Core? i.e. the docs for binding to an object graph suggests this should work:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    var emailConfig = new EmailConfig();
    var emailSection = Configuration.GetSection("Email");
    emailSection.Bind(emailConfig);

    app.UseServiceStack(new AppHost
    {
        AppSettings = new NetCoreAppSettings(Configuration)
    });
}

Which is effectively what NetCoreAppSettings does behind the scenes. Do you know how you would populate EmailConfig with .NET Core’s API?

I have never personally used that, but I am aware how it works and how to use it.

This was annoying, after an exhausting round of trial & error I accidentally discovered that it apparently matters where "Email" is defined in your appsettings.json, where if you move it to the top, e.g:

{
  "Email": {
    "From": "noreply@myapp.com",
    "ApiKey":  "EMPTY"
  },
  "ConnectionStrings": {
    "MainDatabase": "....."
  },
}

It starts working where you can bind it to a Typed POCO with ServiceStack’s IAppSettings or .NET Core’s IConfiguration APIs, e.g:

var emailConfig = AppSettings.Get<EmailConfig>("Email");
emailConfig.PrintDump();

This is very strange, when I do the following:

var emailConfigFromAsp = new EmailConfig();
            var emailConfigFromServiceStack = AppSettings.Get<EmailConfig>("Email");

            var section = config.GetSection("Email");

            section.Bind(emailConfigFromAsp);

            emailConfigFromAsp.PrintDump();
            emailConfigFromServiceStack.PrintDump();

The emailConfigFromAsp is populated, but the one from ServiceStack is not.

Digging deeper into AppSettings, I see that the NetCoreAppSettings.Configuration.ConfigurationRoot has several providers, with two being JsonConfigurationProviders. One of those has 0 keys, the other has 8 (and includes my Email section).

I’ve changed back to using the default WebHost.CreateDefaultBuilder (which adds the default json configs of appsettings.json and appsettings.Environment.json). My guess is that since I have nothing in appsettings.Debug.json the second one is empty. But you still call the Bind api in GetSettings the same as I did here so I’m not sure what’s going on.

@mythz Did you ever figure out why this weirdness was happening?

IAppSettings is just calling .NET Core’s .Bind<T>() API above where both started working after moving the “Email” configuration to the top of appsettings.json, I don’t know why.

1 Like