Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

POCO object array inside AppSettings.json in ASP.NET Core

This seems like it should be really simple, I have been searching SO and a lot of other places for an answer to this, everything I have found and tried does not work.

I have an appsettings.json file that looks like this

"Email": {
"Port": "25",
"Host": "localhost",
"EnableSSL": "false",
"Credentials": {
  "Username": "fakeuser",
  "Password": "fakepassword"
},
"SystemFromAddress": "[email protected]",
"SystemFromDisplayName": "Test Sender",
"EmailTemplateRootDirectory": "Email\\EmailTemplates",
"EmailTemplates": [
  {
    "TemplateKey": "ResetPassword",
    "TemplatePath": "ResetPassword.cshtml"
  },
  {
    "TemplateKey": "NewAccount",
    "TemplatePath": "NewAccount.cshtml"
  },
  {
    "TemplateKey": "VerifyEmail",
    "TemplatePath": "VerifyEmail.cshtml"
  }
]

}

There are several models (EmailOptions being the parent) that I am trying to bind to, the EmailOptions class is expecting to have it's EmailTemplates list populated from the EmailTemplates list in the appsettings.json as seen above.

The parent class is being populated by the appsettings.json file as expected, the Child List of Email Templates in this class is always coming up empty.

Here are the classes I am binding to.

public class EmailOptions
{
    public int Port { get; set; }
    public string Host { get; set; }
    public bool EnableSSL { get; set; }
    public EmailCredentials Credentials { get; set; }
    public string SystemFromAddress { get; set; }
    public string SystemFromDisplayName { get; set; }
    public string EmailTemplateRootDirectory { get; set; }

    public IEnumerable<EmailTemplate> EmailTemplates { get; set; } = new List<EmailTemplate>();

}

public class EmailTemplate
{
    public string TemplateKey { get; set; }
    public string TemplatePath { get; set; }
}

public class EmailCredentials
{
    public string Username { get; set; }
    public string Password { get; set; }
}

I am using the following call I am making in my startup class in ASP.NET Core.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddOptions();
        services.Configure<EmailOptions>( _configuration.GetSection("Email" ));
        ...

For some reason the IEnumerable property in my EmailOptions is not being deserialized from the appsettings.json into my options - when I attempt to use it anywhere in my controllers - the list is always set to an empty array.

FWIW: I have this working in a console application where I have more control over setting up my options from the appsettings.json. Here is what I am doing in the console app, (I am leaving out the code where I set up the options with the DI container for brevity)

var emailSection = configuration.GetSection( "Email" );
var emailOptions = emailSection.Get<EmailOptions>();

emailOptions.EmailTemplates = configuration.GetSection( "Email:EmailTemplates" ).Get<List<EmailTemplate>>();

as expected - in the console application, I get my Email Templates because i have the ability to get the child list separately and add it to the options before handing it over to the DI container. I don't seem to have that flexibility in the ASP.NET Core IServiceCollection.Configure() extension method (so maybe use another method to do this? which one? After a couple hours of searching I am crying uncle and asking for help).

So how does one get this to work using the ASP.NET Core "IServiceCollection.Configure()" method? Is there a better way to do this?

like image 973
Chase Thomas Avatar asked Jan 03 '18 18:01

Chase Thomas


1 Answers

Thank you Joe for pointing out what needed to happen!

I made the false assumption that the serializer would happily create it's list from the json and assign that list to my IEnumerable - rather - you need to make sure to use List if you intend to deserialize a list of json objects into your Options (and other concrete dotnet types where applicable).

so instead of this

IEnumerable<EmailTemplate> EmailTemplates { get; set; }    

I should have had this...

List<EmailTemplate> EmailTemplates { get; set; }
like image 165
Chase Thomas Avatar answered Oct 21 '22 18:10

Chase Thomas