dotnet / dotnet/EntityFramework.Docs

EFCore 6 Table Splitting regression bug with Project <Nullable>disable</Nullable>

Open
#3,951 2 comments 0 reactions 0 assignees View on GitHub
area-model-building
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

## Table Splitting "OrderDetails" entity returning null when upgrading project from .Net 5 with Nullable disabled

### Bug
- Expected behaviour - DetailedOrder should be returned as it includes non-nullable Status property set to 1 as per guidance https://docs.microsoft.com/en-us/ef/core/modeling/table-splitting
- Actual behaviour - EFCore 6 does not return DetailedOrder (i.e. returns null)

### Setup
- The below code works as expected with EFCore 5, or EFCore 6 when project is configured `enable` (and after changing DetailedOrder.Content to nullable _byte[]?_)
- Exception thrown with EFCore 6 when project is configured `disable` (which is default when upgrading from .Net 5 and older)

### Code
```C#
using Microsoft.EntityFrameworkCore;

using (var db = new DemoDbContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();

var order = new Order
{
Status = 1,
DetailedOrder = new DetailedOrder { Status = 1 }
};

db.Add(order);
db.SaveChanges();

var detailedOrder = db.DetailedOrders.FirstOrDefault();
if (detailedOrder == null)
{
throw new Exception("DetailedOrder should be returned due to non-nullable Status");
}
else
{
Console.WriteLine($"Successfully returned {nameof(DetailedOrder)} Id {detailedOrder.Id}");
}
}

public class DemoDbContext : DbContext
{
public DbSet Orders { get; set; }
public DbSet DetailedOrders { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Data Source=demodbcontext.db");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity(dob =>
{
dob.ToTable("Orders");
dob.Property(o => o.Status).HasColumnName("Status");
});

modelBuilder.Entity(ob =>
{
ob.ToTable("Orders");
ob.Property(o => o.Status).HasColumnName("Status");
ob.HasOne(o => o.DetailedOrder).WithOne()
.HasForeignKey(o => o.Id);
});
}
}

public class Order
{
public int Id { get; set; }
public int Status { get; set; }
public DetailedOrder DetailedOrder { get; set; }
}

public class DetailedOrder
{
public int Id { get; set; }
public int Status { get; set; }
public byte[] Content { get; set; }
}
```
### Workaround Notes
Changing project to `enable` is clearly not an acceptable workaround, as it changes the runtime behaviour (as demonstrated here) and can cause other regression bugs (with other frameworks).

With .Net 6 Nullable disabled, even after telling ModelBuilder that `Content` is not required `dob.Property(o => o.Content).IsRequired(false);` does not help

### Include provider and version information
EF Core version: 6.0.6
Database provider: Microsoft.EntityFrameworkCore.Sqlite
Target framework: .NET 6.0 `disable`
Operating system: Win 10
IDE: Visual Studio 2022

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.