'Can I use C# 9 records as IOptions?

I have just started playing around with C# 9 and .NET 5.0, specifically the new record construct. I find I have a lot of excellent use cases for the shorthand syntax of the record types.

One of the use cases I've considered was using a record for the dto in IOptions<>, instead of regular classes, for ASP.NET Core applications. These option classes are usually quite plain so I thought it would be a perfect fit, but it seems I cannot get it to work easily, since configuring the application using IOptions<> requires the object to have a parameterless constructor.

public record MyOptions(string OptionA, int OptionB);

public class Startup
{
    public void ConfigureServices(IServiceCollection services) {
        services.Configure<MyOptions>(Configuration.GetSection(nameof(MyOptions)));
    }
...
}

public class MyController : Controller 
{
    private readonly MyOptions _options;
    public MyController(IOptions<MyOptions> options) {
        _options = options.Value;  // This throws an exception at runtime
    }
}

The example above throws the following exception when attempting to access the IOption<>.Value property:

System.MissingMethodException: 'No parameterless constructor defined for type 'AcmeSolution.MyOptions'.'

Is there any way to configure the IOptions configuration system to deserialize the options using the record's constructor instead of requiring a parameterless constructor?

I could use the longhand syntax for the records, but then there's really no benefit over using a class.



Solution 1:[1]

Is there any way to configure the IOptions configuration system to deserialize the options using the record's constructor instead of requiring a parameterless constructor?

No, in general ASP.Net Core uses a lot of run-time type instancing, which requires constructor calls that are known beforehand, and in this case it requires a constructor with no arguments.

You can however make your record class have a parameter-less constructor:

public record MyOptions(string OptionA, int OptionB)
{
    public MyOptions(): this(default, default) {}
}

Is it worth it? Eh. Up to you. It's no worse than a regular class, performance-wise, so go with whatever you find clearer!

Edit: alternatively, you should be able to use this form:

public record MyOptions(string OptionA = default, int OptionB = default);

Solution 2:[2]

C# 10

I know the question specifically references C# 9, but if you're living in the present, which I suspect most of you are, this works as expected with C# 10 (ASP.NET Core 6):

public record MyOptions
{
  public string MyProperty { get; init; }
}

// ...

builder.Services.Configure<MyOptions>(builder.Configuration);

Solution 3:[3]

Add parameter less constructor

public MyOptions() {}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Blindy
Solution 2 Brandon Gano
Solution 3 sadiq rashid