dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.72k stars 3.17k forks source link

Complex property defined in base abstract class not being mapped in TPC #34763

Open NikolaVranes opened 3 weeks ago

NikolaVranes commented 3 weeks ago

I am encountering an issue with Entity Framework Core (EF Core) when using the Table-Per-Class (TPC) inheritance strategy. Specifically, the complex Timestamp property, defined in the base abstract class (AbstractType), is returning default values for it's properties for derived entities (ConcreteTypeA and ConcreteTypeB) during queries, while for ConcreteTypeC it's working as expected. ConcreteTypeC has no additional properties, and the timestamps are being mapped good, but on the other side ConcreteTypeA and ConcreteTypeB have additional properties and the timestamps are being defaulted since DateTime is a struct. I looked into the problem and debugged the queries that are generated. EF is generating a UNION in the SQL and the TimeStamp columns where being mapped in the following way. c."Timestamp_Local", c."Timestamp_Utc" on table C, and NULL AS "Timestamp_Local", NULL AS "Timestamp_Utc" on tables A and B.

[ComplexType]
public class DbTimestamp : IComparable<DbTimestamp>
{
    private DateTime _utc;
    public DateTime Utc
    {
        get => _utc;
        set => _utc = DateTime.SpecifyKind(value, DateTimeKind.Utc);
    }
    private DateTime _local;
    public DateTime Local
    {
        get => _local;
        set => _local = DateTime.SpecifyKind(value, DateTimeKind.Utc);
    }
}

internal abstract class AbstractType
{
    [Key]
    [MaxLength(36)]
    public required string Id { get; init; }
    public required DbTimestamp Timestamp { get; init; }
}

private static ModelBuilder Build(this ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AbstractType>(entity =>
        {
            entity.Property(e => e.Id).ValueGeneratedNever();
            entity.UseTpcMappingStrategy();
            entity.Ignore(e => e.Timestamp);
        });
        modelBuilder.Entity<ConcreteTypeA>(builder =>
        {
            builder.ToTable("ConcreteTypeA");
            builder.ComplexProperty(e => e.Timestamp);
        });
        modelBuilder.Entity<ConcreteTypeB>(builder =>
        {
            builder.ToTable("ConcreteTypeB");
            builder.ComplexProperty(e => e.Timestamp);
        });
        modelBuilder.Entity<ConcreteTypeC>(builder =>
        {
            builder.ToTable("ConcreteTypeC");
            builder.ComplexProperty(e => e.Timestamp);
        });   
        return modelBuilder;
    }
NikolaVranes commented 3 weeks ago

Additional information, insert commands are working as expected, only fetching data from DB isn't working.

cincuranet commented 1 week ago

This issue is lacking enough information for us to be able to fully understand what is happening. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.