我通常对 EF 实体使用以下接口(.NET 9、EF 9):
public interface IEntity
{
// 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()
{ }
我现在正在考虑为所有实体使用具体的基类。考虑以下层次结构:
// 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; }
}
需要注意的几件事:
DocumentBase
和ElementBase
都不需要是抽象的(如果需要,我们应该可以选择将它们标记为抽象)。DbContext
除了它们的属性之外,应该对这些基类一无所知(就像它们是在具体类中声明的一样)。ConcreteEntity > ConcreteEntityBase > EntityBase
。无需考虑其他路径。生成的表应具有以下声明,没有鉴别器列(无 TPH、TPT):
Document (Id, IsAuditable, DateTimeCreated, DateTimeModified, Name, Elements)
Element (COLUMNS: Id, IsAuditable, DateTimeCreated, DateTimeModified, Name, Document, DocumentId)
我发现的最好的资源是这个博客讨论
Table per Concrete Type (TPC)
,但它已经有十多年的历史了,而我正在尝试理解 EF 9。
请注意,这个问题与设计选择或我的方法是否符合最佳实践无关。我只是想知道EF9是否可以实现,如果可以,需要什么配置。
您不需要为 EF 定义任何继承配置来执行您期望的操作,只要您没有为基类声明任何
DbSet
或实体配置即可。您可能需要的任何显式配置(数据类型、列命名等)只需通过最终子类完成,而不是公共基类。
我的“可编辑”实体项目有类似的基类,它集中了 CreatedByUserId、CreatedDateTime 等公共字段。基本 EnditableEntity 没有表或任何配置,所有配置都基于子类。在我的例子中,EditableEntity 是
abstract
,因为它几乎应该是这样,但我不认为这会直接影响映射。直接引用基类的任何配置或 DbSet 可能会干扰映射并使 EF 期望 TPH/TPT/TPC 配置。