dotnet / EntityFramework.Docs

Documentation for Entity Framework Core and Entity Framework 6
https://docs.microsoft.com/ef/
Creative Commons Attribution 4.0 International
1.63k stars 1.96k forks source link

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

Open stevozilik opened 2 years ago

stevozilik commented 2 years ago

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

Bug

Setup

Code

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<Order> Orders { get; set; }
    public DbSet<DetailedOrder> 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<DetailedOrder>(dob =>
        {
            dob.ToTable("Orders");
            dob.Property(o => o.Status).HasColumnName("Status");            
        });

        modelBuilder.Entity<Order>(ob =>
            {
                ob.ToTable("Orders");
                ob.Property(o => o.Status).HasColumnName("Status");
                ob.HasOne(o => o.DetailedOrder).WithOne()
                    .HasForeignKey<DetailedOrder>(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 <Nullable>enable</Nullable> 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 <Nullable>disable</Nullable> Operating system: Win 10 IDE: Visual Studio 2022

AndriySvyryd commented 2 years ago

Try ob.Navigation(o => o.DetailedOrder).IsRequired();

stevozilik commented 2 years ago

That helped thank you @AndriySvyryd

Worth updating the documentation? https://docs.microsoft.com/en-us/ef/core/modeling/table-splitting