C# nullable refs and virtual vs abstract properties

Consider:

public class InitialStep2Sqlite : ScriptMigration
{
    protected override string UpPath => "Sqlite2.Up.sql";
    protected override string DownPath => "Sqlite2.Down.sql";
}

public abstract class ScriptMigration : Migration
{
  protected virtual string UpPath {get;}
  protected virtual string DownPath {get;}

  protected override void Up(MigrationBuilder mb) 
    => mb.RunSqlFile(UpPath);
  protected override void Down(MigrationBuilder mb) 
    => mb.RunSqlFile(DownPath);
}

which, compiled under nullable enable, says “[CS8618] Non-nullable property 'UpPath' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.”

Changing the property's virtual to abstact removes the warning.

The lesson: If your non-null proof of a property depends crucially on the subclass making it non-null, then making the parent property abstract not virtual is the correct statement of intent.

Generally, if a parent Member depends on the subclass for correctness – if the parent can't provide a correct implementation itself – then, logically, its correctness requires it to be abstract not virtual.

.Net Core Strong Typed Configuration Binding for Arrays | Microsoft.Extensions.Configuration

The .Net Core Microsoft.Extensions.Configuration package includes the ConfigurationBinder.Get<>() method which can read a config file and bind name-value entries to a class. This gives you strongly typed configuration:

public class FromTo
{
    public string From { get; init; }
    public string To { get; init; }
}

The method is an extension method for Configuration so you can call it straight off a Configuration instance. In an Asp.Net Startup.cs class, for instance:

services.AddSingleton(
    s=>Configuration
            .GetSection("fromto").Get<FromTo>());

will result in your services collection knowing to provide an FromTo class with the Properties populated from the configuration entries, matched by section:propertyname :

{
  "fromto:from": "[email protected]",
  "fromto:to": "[email protected]"
}

or if you use secrets:

cd <projectdirectory>
dotnet user-secrets init
dotnet user-secrets set fromto:from [email protected]
dotnet user-secrets set fromto:to [email protected]

That works great for the usual primitives - string, numbers, boolean — but what about binding an Array?

public class FromTo
{
    public string From { get; init; }
    public string To[] { get; init; }
}

the From field is still bound but the To field silently fails and results in a null value.

The magic trick is to add a colon-separated number suffix to each setting name:

{
  "fromto:from": "[email protected]",
  "fromto:to:0": "[email protected]",
  "fromto:to:1": "[email protected]",
  "fromto:to:2": "[email protected]",
}

Now Configuration.GetSection("fromto").Get<FromTo>() will successfully bind all 3 rows for "fromto:to into the public string To[] array.