Loading derived entity
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Minimal reproducible example
please see [minimal reproducible example](https://github.com/enterprisebug/EfCoreMinimalReproducibleExample)
DemoDbContext .cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EfCoreMinimalReproducibleExample
{
public class DemoDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
optionsBuilder.UseSqlServer("Server=127.0.0.1,5433;Initial Catalog=DemoDbContextTable;User Id=sa;Password=Pass@word;MultipleActiveResultSets=true");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(b =>
{
b.ToTable("BaseEntities");
b
.HasDiscriminator("Discriminator")
.HasValue("ServiceContractCalculationInformationDiscriminator")
.HasValue("MaintenanceCalculationInformationDiscriminator");
});
modelBuilder.Entity(b =>
{
b.OwnsOne(x => x.OperatingHourRange, ob =>
{
ob.Property(x => x.Start).HasConversion(ValueTypeConverters.OperatingHoursValueConverter);
ob.Property(x => x.End).HasConversion(ValueTypeConverters.OperatingHoursValueConverter);
});
b.Property(x => x.RunTimePerYearAndEngine).HasConversion(ValueTypeConverters.OperatingHoursValueConverter);
});
}
public DbSet CalculationInformations => Set();
public DbSet DerivedEntity1s => Set();
public DbSet MaintenanceCalculationInformations => Set();
public DbSet RootEntities => Set();
}
public static class ValueTypeConverters
{
public static ValueConverter OperatingHoursValueConverter { get; } = new(
v => v.Value,
v => new OperatingHours(v)
);
}
public class RootEntity {
protected RootEntity() => (BaseEntity) = (null!);
public RootEntity(CalculationInformation baseEntity)
{
BaseEntity = baseEntity;
}
public Guid Id { get; set; }
public virtual CalculationInformation BaseEntity { get; private set; }
}
public abstract class CalculationInformation
{
protected CalculationInformation() { }
public Guid Id { get; private set; }
internal abstract OperatingHours OverallOperatingHours { get; }
}
public class ServiceContractCalculationInformation : CalculationInformation
{
protected ServiceContractCalculationInformation() => (OperatingHourRange, RunTimePerYearAndEngine) = (null!, null!);
internal ServiceContractCalculationInformation(OperatingHourRange operatingHourRange, OperatingHours runTimePerYearAndEngine) : base()
{
OperatingHourRange = operatingHourRange;
RunTimePerYearAndEngine = runTimePerYearAndEngine;
}
public OperatingHourRange OperatingHourRange { get; private set; }
public OperatingHours RunTimePerYearAndEngine { get; private set; }
internal override OperatingHours OverallOperatingHours => OperatingHours.Zero;
}
public class MaintenanceCalculationInformation : CalculationInformation
{
protected internal MaintenanceCalculationInformation() : base()
{
}
internal override OperatingHours OverallOperatingHours => OperatingHours.Zero;
}
}
public record OperatingHours
{
public OperatingHours(int value)
{
Value = value;
}
public static OperatingHours Zero => new(0);
public int Value { get; }
}
public record OperatingHourRange
{
// if the next line is uncommented it works. Why?
//protected OperatingHourRange() => (Start, End) = (null!, null!);
public OperatingHourRange(OperatingHours start, OperatingHours end)
{
Start = start ?? throw new ArgumentNullException(nameof(start));
End = end ?? throw new ArgumentNullException(nameof(end));
}
public OperatingHours Start { get; private init; }
public OperatingHours End { get; private init; }
}
Program.cs
using EfCoreMinimalReproducibleExample;
using var ctx = new DemoDbContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var maintenanceCalculationInformation = new MaintenanceCalculationInformation();
var rootEntity = new RootEntity(maintenanceCalculationInformation);
await ctx.AddAsync(rootEntity);
await ctx.SaveChangesAsync();
using var newCtx = new DemoDbContext();
var loaded = newCtx.RootEntities.First();
Console.WriteLine(loaded.BaseEntity);
### Stacktrace
```
System.ArgumentNullException: 'Value cannot be null. (Parameter 'start')'
at OperatingHourRange..ctor(OperatingHours start, OperatingHours end) in C:\Source\Repos\EfCoreMinimalReproducibleExample\EfCoreMinimalReproducibleExample\DemoDbContext.cs:line 117
at Castle.Proxies.OperatingHourRangeProxy..ctor(IInterceptor[] , OperatingHours start, OperatingHours end)
```
### Observations
#### Run with "F5".
Error that is thrown is:

#### Behavior when inspecting the variable in debug session
if you set a debugger breakpoint in `Program.cs:16` and hover over `loaded.BaseEntity` you see:

if you then continue the execution no error occurs.

#### Interesting observation
if you uncomment `DemoDbContext.cs:114`, run with "F5" no error occurs
### Include provider and version information
EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: net6.0
Operating system: Windows 21H2 (OS Build 19044.1526)
IDE: Visual Studio 2022 17.0.4
Contributor guide
Assessment
This issue has not been assessed yet.