EF Core Code-First模式下多表共享相同字段的数据库设计方案咨询
方案合理性分析
你当前用的层级继承方案是可以实现需求的,但是有两个需要注意的配置点:
- EF Core默认采用TPH(每层次结构一张表)的继承映射策略,如果直接按你写的代码生成迁移,会默认生成一张包含所有子类、父类属性加
Discriminator区分字段的表,不符合你要purchases和products独立表的需求。你需要给所有基类(Prices/PricesAndDates/PricesDatesAndContent)添加[NotMapped]特性,或者在DbContext.OnModelCreating中配置忽略这些基类的映射,这样EF Core会把所有继承来的属性直接映射到子类对应的独立表中,就能实现一处修改基类、所有关联表同步更新的效果。 - 这个方案的局限性是受C#单继承限制,后续如果有实体需要复用多组不相关的公共属性(比如某类实体需要价格属性但不需要日期,另一类需要审计属性但不需要价格),继承链会很难调整,灵活性较差。
更灵活的替代方案:使用EF Core原生Owned Types(拥有的实体类型)
这个方案不需要依赖继承,专门用于属性复用场景,支持任意组合公共属性组,扩展性更强:
步骤1:拆分公共属性为独立类
// 价格公共属性 public class PriceInfo { public double Price1 { get; set; } public double Price2 { get; set; } } // 日期公共属性 public class DateInfo { public DateTime SomeDate1 { get; set; } public DateTime SomeDate2 { get; set; } } // 内容公共属性 public class ContentInfo { public string Name { get; set; } public string Description { get; set; } }
步骤2:业务实体引用公共属性类
public class Purchase { public int Id { get; set; } // 引用公共属性 public PriceInfo Price { get; set; } public DateInfo Dates { get; set; } public ContentInfo Content { get; set; } // Purchase特有属性 public string PurchaseNo { get; set; } } public class Product { public int Id { get; set; } // 引用公共属性 public PriceInfo Price { get; set; } public DateInfo Dates { get; set; } public ContentInfo Content { get; set; } // Product特有属性 public string ProductSn { get; set; } }
步骤3:配置Owned Type映射
在DbContext.OnModelCreating中配置,EF Core会自动把Owned Type的所有属性映射到主实体对应的表中,不需要关联表:
// 可以写扩展方法批量配置,减少重复代码 public static class ModelBuilderExtensions { public static EntityTypeBuilder<T> MapCommonProperties<T>(this EntityTypeBuilder<T> builder) where T : class { builder.OwnsOne(x => x.Price); builder.OwnsOne(x => x.Dates); builder.OwnsOne(x => x.Content); // 如果不需要字段名带导航属性前缀(比如不要Price_Price1,想要直接Price1),可以单独配置字段名 // builder.OwnsOne(x => x.Price, p => // { // p.Property(x => x.Price1).HasColumnName(nameof(PriceInfo.Price1)); // p.Property(x => x.Price2).HasColumnName(nameof(PriceInfo.Price2)); // }); return builder; } } // 调用配置 protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Purchase>().MapCommonProperties(); modelBuilder.Entity<Product>().MapCommonProperties(); }
这个方案的优势是可以自由组合需要的公共属性组,不受单继承限制,后续新增公共属性组只需要新增独立类,按需添加到对应实体即可,维护成本更低。
选择建议
- 如果你当前的公共属性分组逻辑非常固定,未来不会有调整需求,直接给现有基类加
[NotMapped]即可,现有方案可以正常使用 - 如果后续可能有灵活组合公共属性的需求,更推荐使用Owned Types方案
内容的提问来源于stack exchange,提问作者user62863
相关产品推荐
相关产品推荐

