My understanding of C#'s null safety features (from C# 8.0 onwards) is that (possibly) null variables must be typed as nullable and that the compiler will statically detect any parts of the code which may involve a null reference dereference and issue a warning. Coupled with <WarningsAsErrors>Nullable</WarningsAsErrors>, I would expect this to make it impossible for null reference exceptions to occur.
However, the following code compiles but when a Get request is sent to /endpoint, it returns a null reference exception:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
var connString = "User ID=postgres;Password=password;Host=localhost;Port=5432;Pooling=true;Connection Lifetime=0;Database=MinExample";
builder.Services.AddDbContext<AppDbContext>(options => options.UseNpgsql(connString));
var app = builder.Build();
app.MapGet("/endpoint", long (AppDbContext context) =>
{
var id = new Service(context).GetBId();
return id;
});
app.Run();
public record A
{
public long AId { get; init; }
public required B B { get; set; }
}
public class B
{
public required long BId { get; init; }
}
public class Service
{
private readonly AppDbContext _context;
public Service(AppDbContext cx)
{
_context = cx;
}
public long GetBId()
{
var a = _context.A.First();
return a.B.BId;
}
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<A> A { get; set; }
}
My database has a single entry in the A table and a single related entry in the B table.
In fact, the compiler is so confident that id is not null that if I insert Console.WriteLine(id is null) just before the return statement of the API, the compiler fails with the error Cannot convert null to 'long' because it is a non-nullable value type. On the other hand, Console.WriteLine(id == null) compiles and runs fine (and writes True to the console) although JetBrains warns me that The result of the expression is always 'false' because a value of type 'long' is never equal to 'null' of type 'long?'
Why does the compiler not give me an error that the variable I am returning is null? Is this a null-safety violation? What am I missing about how null-safety works in C# 8.0 and up?
(The "solution" to this problem is to replace the query with var a = _context.A.Include(a => a.B).First();, but my question is about why the compiler does not detect that something is wrong, not about how to fix the problem itself).
All of the above has been build with dotnet version 9.0.112.
.AutoInclude().