为了避免使用按表分层结构(TPH),我一直在研究如何在数据库模型中最佳实现按表具体类(TPC)继承的示例。我遇到了official documentation和this article。
下面是一些具有一些简单继承的模型类。
public class BaseEntity
{
public BaseEntity()
{
ModifiedDateTime = DateTime.Now;
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime ModifiedDateTime { get; set; }
}
public class Person : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Business : BaseEntity
{
public string Name { get; set; }
public string Location { get; set; }
}
这两篇文章中的示例均使用了DbModelBuilder配置。
modelBuilder.Entity<BaseEntity>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
});
modelBuilder.Entity<Business>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Business");
});
该应用程序成功运行,但是当我返回数据库时,我发现三(3)个表,而不是我期望的两(2)个表。经过一些测试后,似乎会创建“BaseEntity”表,但从未使用过。除了这个空的孤立表之外,其他所有东西似乎都可以正常工作。
我弄乱了DbModelBuilder配置,最终删除了提供预期结果的“BaseEntity”配置;两(2)个表,每个表都具有正确的属性并正常运行。
我做最后一个测试,删除所有DbModelBuilder配置,仅包括“人”和“业务”的两(2)个DbSet属性,然后再次测试。
public DbSet<Person> People { get; set; }
public DbSet<Business> Businesses { get; set; }
令我惊讶的是,该项目将构建,然后进入数据库,仅创建具有所有类属性的两个表,包括从“BaseEntity”类继承的表。我可以毫无问题地进行CRUD操作。
在运行了许多测试之后,我找不到最终测试的任何问题,而且我也无法重现两篇文章都警告过的重复键错误。
最佳答案
为了使这一切变得更简单,我已经移动了强制TablePerConcrete开源的必要代码。其目的是允许通常仅在Fluent接口(interface)中可用的功能(您必须在其中将大量代码分散到Db类的OnModelCreating方法中)才能迁移到基于属性的功能。
它允许您执行以下操作:
[TablePerConcrete]
public class MySubclassTable : MyParentClassEntity
不管EF可能决定从您的父类/子类关系中推断出什么,都强制使用TPC。
这里一个有趣的挑战是,有时EF会破坏继承的Id属性,将其设置为使用显式值填充,而不是由数据库生成。您可以通过使父类实现
IId
接口(interface)(它只是说:这具有一个Id属性),然后用[ForcePKId]
标记子类来确保不这样做。public class MyParentClassEntity : IId
{
public int Id { get; set; }
. . .
[TablePerConcrete]
[ForcePKId]
public class MySubclassTable : MyParentClassEntity
{
// No need for PK/Id property here, it was inherited and will work as
// you intended.
开始为您处理所有这些的代码非常简单-只需在Db类中添加几行即可:
public class Db : DbContext
{
. . .
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var modelsProject = Assembly.GetExecutingAssembly();
B9DbExtender.New().Extend(modelBuilder, modelsProject);
您可以通过以下两种方式之一进行访问: