Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a property get initialized to a null instead of an empty list from configuration?

I have an asp.net-core web api project with a configuration containing the following definition:

"MyClass": {
   "Value1": 1,
   "Value2": []
}

This configuration is used to initialize the following class:

public class MyClass
{
    public int Value1 { get; set; }
    public int[] Value2 { get; set; }
}

This class is initialized from configuration using the following code in the ConfigureServices method in Startup.cs:

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

    services.Configure<MyClass>(Configuration.GetSection("MyClass"));
    services.AddSingleton<AService>();

    services.AddMvc();
}

My service constructor looks like this:

public FeedbackRetrievalService(IOptions<MyClass> myclass)
{
    _myclass = myclass;
}

In the constructor I notice that Value2 is null, even though it does appear in the configuration as an empty list - which is the value I would expect the property to be initialized with. I expected that the property would be initialized with null only when the field is missing from the configuration. Is there any way with which I could initialize the field as an empty list?

like image 861
Maor Veitsman Avatar asked Oct 25 '17 14:10

Maor Veitsman


1 Answers

The problem is that the JSON configuration loader and the configuration binder are two separate components. When you load a configuration, the configuration provider will load the whole configuration file into a key/value structure where the key is a property path. In your example, MyClass:Value2:0 and MyClass:Value2:1 could be paths for two array elements in your Value2 JSON array.

The configuration binder will then later interpret that structure and turn it into an object of your class. At that time, it will only have the information available that exists in that key/value structure that was previously loaded.

Now, if you look at the loaded configuration before binding it, you will notice that an empty array does not result in anything. And if you think about how the path works, it makes sense: MyClass:Value2:0 and MyClass:Value2:1 are the values for index 0 and 1 respectively of the Value2 array. But how would this work for an empty array? There simply isn’t an entry for an empty array.

So since the empty array does not appear in the loaded configuration, the binder cannot initialize your array.

What you can do however is set up default values for bound configuration types:

public class MyClass
{
    public int Value1 { get; set; }
    public int[] Value2 { get; set; } = Array.Empty<int>();
}

Now, if you bind your configuration with that empty array to that type, the Value2 will never be touched by the binder, leaving its default value intact.

like image 189
poke Avatar answered Nov 15 '22 03:11

poke