Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I hint the C# 8.0 nullable reference system that a property is initalized using reflection

I ran into an interesting problem when I tried to use Entity Framework Core with the new nullable reference types in C# 8.0.

The Entity Framework (various flavors) allows me to declare DBSet properties that I never initalize. For example:

    public class ApplicationDbContext : IdentityDbContext
      {
    #pragma warning disable nullable
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
          : base(options)
        { }
    #pragma warning restore nullable

        public DbSet<Probe> Probes { get; set; }
        public DbSet<ProbeUnitTest> ProbeUnitTests { get; set; }
    }

The DbContext constructor reflects over the type and initializes all of the DbSet properties, so I know that all the properties will be non-null by the conclusion of the constructor. If I omit the #pragma's i get the expected warnings because my code does not initialize these properties.

 Data\ApplicationDbContext.cs(10,12,10,32): warning CS8618: Non-nullable property 'Probes' is uninitialized.
 Data\ApplicationDbContext.cs(10,12,10,32): warning CS8618: Non-nullable property 'ProbeUnitTests' is uninitialized.

Turning off the warnings seems like a blunt instrument when all I want to do is inform the compiler that a property will not be null?

If turns out I can fool the compiler like this:

     public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
      : base(options)
    {
      Probes = Probes;
      ProbeUnitTests = ProbeUnitTests;
    }

This code has the advantage that it is very narrow -- it only applies to the specific property's initialization and will not suppress other warnings. The disadvantage is that this is nonsense code because assigning a property to itself really ought to do nothing.

Is there a preferred idiom for informing the compiler that it just does not know that the property has been initialized?

like image 327
John Melville Avatar asked Aug 04 '19 00:08

John Melville


People also ask

Is C easy to learn?

While C is one of the more difficult languages to learn, it's still an excellent first language pick up because almost all programming languages are implemented in it. This means that once you learn C, it'll be simple to learn more languages like C++ and C#.


3 Answers

Whenever you want to tell the compiler "shut up, I know what I'm doing" in regards to nullable reference types, use the ! operator. You can fix your issue by declaring your properties like so:

public DbSet<Probe> Probes { get; set; } = null!;
public DbSet<ProbeUnitTest> ProbeUnitTests { get; set; } = null!;
like image 51
Dave M Avatar answered Nov 16 '22 14:11

Dave M


Microsoft has 2 recommendations for nullable references in the context of DbContext and DbSet:

1

The common practice of having uninitialized DbSet properties on context types is also problematic, as the compiler will now emit warnings for them. This can be fixed as follows:

    // ...
    public DbSet<Customer> Customers => Set<Customer>();
    public DbSet<Order> Orders => Set<Order>();
    // ...

2

And the other is what @dave-m answered:

Another strategy is to use non-nullable auto-properties, but to initialize them to null, using the null-forgiving operator (!) to silence the compiler warning.

public DbSet<Customer> Customers { get; set; } = null!;
public DbSet<Order> Orders { get; set; } = null!;
like image 42
João Portela Avatar answered Nov 16 '22 14:11

João Portela


If you are using .NET 5, you can now use the MemberNotNullAttribute to indicate which members will be not null after the method returns.

public class ApplicationDbContext : IdentityDbContext
{
    [MemberNotNull(nameof(Probes))]
    [MemberNotNull(nameof(ProbeUnitTests))]
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
      : base(options)
    { }

    public DbSet<Probe> Probes { get; set; }
    public DbSet<ProbeUnitTest> ProbeUnitTests { get; set; }
}
like image 34
Nick Whaley Avatar answered Nov 16 '22 14:11

Nick Whaley