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?
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#.
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!;
Microsoft has 2 recommendations for nullable references in the context of DbContext and DbSet:
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>();
// ...
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!;
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; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With