asp.net-coreinheritanceentity-framework-core.net-9.0ef-core-9.0

Ignoring a base type in Entity Framework Core 9 code-first


I typically use the following interfaces for EF Core entities (.NET 9, EF Core 9):

public interface IEntity
    where TEntity: class, new()
{
    // Identity column.
    public long Id { get; set; }
    public bool IsAuditable { get; set; }
    public DateTime DateTimeCreated { get; set; }
    public DateTime? DateTimeModified { get; set; }
}

public interface IEntity<TEntity>:
    IEntity
    where TEntity: class, IEntity, IEntity<TEntity>, new()
{ }

I am now considering using concrete base classes for all entities.

Consider the following hierarchy:

// Acts as a based class for all entities in the DbContext.
public class EntityBase<TEntity>:
    IEntity<TEntity>
    where TEntity : class, IEntity, IEntity<TEntity>, new()
{
    // Identity column.
    public long Id { get; set; }
    public bool IsAuditable { get; set; }
    public DateTime DateTimeCreated { get; set; }
    public DateTime? DateTimeModified { get; set; }
}

public class DocumentBase : EntityBase<DocumentBase>
{
    public string Name { get; set; }
}

public class ElementBase : EntityBase<ElementBase>
{
    public string Name { get; set; }
}

// Should represent a database table with all properties
// from the base class but without a discriminator column.
public class Document : DocumentBase
{
    public virtual ICollection<Element> Elements { get; set; } = [];
}

// Should represent a database table with all properties
// from the base class but without a discriminator column.
public class Element : ElementBase
{
    public long DocumentId { get; set; }
    public virtual Document Document { get; set; }
}

public class ApplicationDbContext : DbContext
{
    public DbSet<Document> Documents { get; set; }
    public DbSet<Element> Elements { get; set; }
}

A couple of things to notice:

The resulting tables should have the following declarations WITHOUT a discriminator column (no TPH, TPT):

Tables:

The best resource I found was this blog discussing Table per Concrete Type (TPC), but it is over a decade old, while I am trying to make sense of EF Core 9.

Please note that this question is not about design choices or whether my approach aligns with best practices. I simply want to know whether this can be achieved with EF9 and, if so, what configuration is needed.


Solution

  • You shouldn't need to define any inheritance configuration for EF to do what you expect, so long as you don't have any DbSet or entity configuration declared for the base class. Any explicit configuration you might need (data types, column naming, etc.) simply do through the end sub-classes, not the common base class.

    I have similar base classes for my projects for "Editable" entities which centralizes common fields like CreatedByUserId, CreatedDateTime, etc. The base EnditableEntity does not have a table or any configuration, all configuration is based on the sub-classes. In my case the EditableEntity is abstract as it pretty much should be, but I don't think that directly affects the mapping. What likely would interfere with mapping and have EF expecting a TPH/TPT/TPC config would be any configuration or DbSet referencing the base class directly.