6

I stumbled across an issue when using the Options pattern where model properties are being set to null when bound to an empty JSON array.

appsettings.json:

{
  "MyOptions": {
    "Values": []
  }
}

MyOptions.cs:

public sealed class MyOptions
{
  public IEnumerable<string> Values { get; set; }
}

Startup.cs:

...
services.Configure<MyOptions>(                            
  _configuration.GetSection(nameof(MyOptions));
...

The above configuration successfully builds and injects an IOptions<MyOptions> when required, however Values property is set to null rather than an empty enumerable. This would cause the following code to throw a NullReferenceException:

public class MyService
{
  public MyService(IOptions<MyOptions> options)
  {
    var values = options.Value.Values;

    foreach (var val in values)
    {
      // Do something
    }
  }
}

This has been raised as an issue on the dotnet repo (https://github.com/dotnet/extensions/issues/1341) although MS seem to have closed it as "working as designed".

Is there a workaround to prevent the NullReferenceException being thrown?

2 Answers 2

4

I always make sure my properties inside a configuration class have meaningful default values assigned:

public sealed class MyOptions
{
  public IEnumerable<string> Values { get; set; } = Array.Empty<string>();
}

This way I don't have to check for null or not configured values each time I'm using my configuration object.

Sign up to request clarification or add additional context in comments.

1 Comment

I prefer this approach to mine, it's a lot cleaner.
1

I think that MS gave a right answer "working as designed". You have allways remember Murphy's Law - anything that can go wrong will go wrong. To create the robust code anyone should expect null value for any nullable property, doesn't matter how it was init. It can always became null somewhere on the way. So I am always checking for null

  if (options.Value.Values != null)
    foreach (var val in options.Value.Values)
    {
       // Do something
    } else ... return error;
       

I don' t know how myoptions are important for this application, but I usually check appdata data in startup already

var myOptions = Configuration.GetSection(nameof(MyOptions));
if (myOptions.Exists())
{
    services.Configure<MyOptions>(myOptions);
    services.AddScoped(typeof(MyService));
} else ... return error

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.