LEFT JOIN with GroupBy subquery throws InvalidOperationException on NULL values from non-matching rows
Moved from npgsql/efcore.pg#3801, reported by @lttruc1402.
When performing a LEFT JOIN against a GroupBy subquery containing aggregate functions (like Count()), EF Core's materializer attempts to read NULL values (from non-matching LEFT JOIN rows) into non-nullable int properties, crashing with InvalidOperationException: Nullable object must have a before the user's null-check can be evaluated.value
This is not provider- it reproduces on SQLite, SQL Server, and PostgreSQL alike.specific
Minimal repro (SQLite)
using Microsoft.EntityFrameworkCore;
await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
context.PickupStatuses.AddRange(
new PickupStatus { PickupStatusId = 1, Name = "Active" },
new PickupStatus { PickupStatusId = 2, Name = "NoRequests" }
);
context.PickupRequests.Add(new PickupRequest { PickupStatusId = 1 });
await context.SaveChangesAsync();
var statusWithCount = context.PickupRequests
.GroupBy(
o => o.PickupStatusId,
(pickupStatusId, el) => new { pickupStatusId, Count = el.Count() });
// Fails with both LeftJoin and GroupJoin+DefaultIfEmpty
var result = await context.PickupStatuses
.LeftJoin(
statusWithCount,
e => e.PickupStatusId,
e => e.pickupStatusId,
(status, countInfo) => new { status, countInfo })
.Select(e => new
{
e.status.PickupStatusId,
Count = e.countInfo == null ? 0 : e.countInfo.Count,
})
.ToListAsync();
public class PickupStatus
{
public int PickupStatusId { get; set; }
public string Name { get; set; } = "";
}
public class PickupRequest
{
public int Id { get; set; }
public int PickupStatusId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<PickupStatus> PickupStatuses { get; set; }
public DbSet<PickupRequest> PickupRequests { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=repro.db");
}
Generated SQL
SELECT "p"."PickupStatusId", "p1"."pickupStatusId", "p1"."Count"
FROM "PickupStatuses" AS "p"
LEFT JOIN (
SELECT "p0"."PickupStatusId" AS "pickupStatusId", COUNT(*) AS "Count"
FROM "PickupRequests" AS "p0"
GROUP BY "p0"."PickupStatusId"
) AS "p1" ON "p"."PickupStatusId" = "p1"."pickupStatusId"
Exception
System.InvalidOperationException: Nullable object must have a value.
at lambda_method62(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](...)
Notes
LEFT JOIN with GroupBy subquery throws InvalidOperationException on NULL values from non-matching rows
Moved from npgsql/efcore.pg#3801, reported by @lttruc1402.
When performing a LEFT JOIN against a GroupBy subquery containing aggregate functions (like
Count()), EF Core's materializer attempts to read NULL values (from non-matching LEFT JOIN rows) into non-nullableintproperties, crashing withInvalidOperationException: Nullable object must have a before the user's null-check can be evaluated.valueThis is not provider- it reproduces on SQLite, SQL Server, and PostgreSQL alike.specific
Minimal repro (SQLite)
Generated SQL
Exception
Notes
GroupJoin+DefaultIfEmptypattern