如何将带条件逻辑的多功能类拆分为特定逻辑的基础类(C# ASP.NET Core)
问题描述
在ASP.NET Core EF应用中,我有一个包含多种关联数据的CentralDesignObject,其存在大量基于自身信息的派生/计算值。该类与DesignType对象为多对一关系,DesignType定义了CentralDesignObject的基础特性。尽管所有DesignType在关联数据类型和计算结果上有诸多相似,但部分计算逻辑会因DesignType不同而存在差异,当前通过计算方法内的条件语句实现。随着DesignType数量增加,条件语句不断膨胀,我希望摆脱这种模式。我考虑将CentralDesignObject核心实现为抽象基类或接口,为每种DesignType创建具体实现类,但疑问RelatedDataA、RelatedDataB等关联数据的外键如何配置(它们可能关联不同的具体实现类)。请问是否有更合适的方案?是否值得进行此改造?期待相关思路与建议。
代码示例
public class CentralDesignObject { public Guid DesignObjectId { get; set; } public int DesignTypeId { get; set; } [ForeignKey("DesignTypeId")] public DesignType DesignType { get; set; } public ICollection<RelatedDataA> RelatedDataA { get; set; } public ICollection<RelatedDataB> RelatedDataB { get; set; } public decimal CalculationMethod_X() { if (DesignType.Name == "foo") { // do x return 0; } else if (DesignType.Name == "bar") { // do y // Slightly different result return 1; } return 0; } } public class RelatedDataA { public int RelatedDataAId { get; set; } public Guid CentralDesignObjectId { get; set; } [ForeignKey("CentralDesignObjectId")] public CentralDesignObject CentralDesignObject { get; set; } public decimal A { get; set; } public decimal B { get; set; } } public class RelatedDataB { public int RelatedDataBId { get; set; } public Guid CentralDesignObjectId { get; set; } [ForeignKey("CentralDesignObjectId")] public CentralDesignObject CentralDesignObject { get; set; } public decimal A { get; set; } public decimal B { get; set; } }
解决方案与思路
1. 采用EF Core的TPH(Table-Per-Hierarchy)继承策略
这是最贴合你想法的方案,EF Core原生支持,能解决关联数据外键的问题:
- 定义抽象基类
CentralDesignObjectBase,包含所有通用属性(DesignObjectId、DesignTypeId、DesignType导航属性、RelatedDataA/RelatedDataB集合) - 为每种
DesignType创建具体子类,比如FooDesignObject、BarDesignObject,在子类中实现各自的CalculationMethod_X逻辑 - EF Core会自动将所有子类映射到同一个数据库表(即原
CentralDesignObject表),通过Discriminator列区分不同类型(你也可以指定用已有的DesignTypeId作为鉴别器) - 关联数据类
RelatedDataA/RelatedDataB的外键仍指向基类的DesignObjectId,EF Core会自动处理不同子类的关联关系,无需修改外键配置
核心代码示例
public abstract class CentralDesignObjectBase { public Guid DesignObjectId { get; set; } public int DesignTypeId { get; set; } [ForeignKey("DesignTypeId")] public DesignType DesignType { get; set; } public ICollection<RelatedDataA> RelatedDataA { get; set; } public ICollection<RelatedDataB> RelatedDataB { get; set; } // 定义抽象计算方法 public abstract decimal CalculationMethod_X(); } public class FooDesignObject : CentralDesignObjectBase { public override decimal CalculationMethod_X() { // 对应原foo类型的计算逻辑 return RelatedDataA.Sum(x => x.A + x.B); } } public class BarDesignObject : CentralDesignObjectBase { public override decimal CalculationMethod_X() { // 对应原bar类型的计算逻辑 return RelatedDataB.Average(x => x.A * x.B); } }
TPH配置(Fluent API)
在DbContext的OnModelCreating中配置鉴别器映射:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<CentralDesignObjectBase>() .HasDiscriminator<int>("DesignTypeId") // 复用已有的DesignTypeId作为鉴别器 .HasValue<FooDesignObject>(1) // 假设foo对应的DesignTypeId为1 .HasValue<BarDesignObject>(2); // 假设bar对应的DesignTypeId为2 }
2. 策略模式(无需修改EF实体结构)
如果不想改动现有实体继承结构,可以用策略模式拆分计算逻辑:
- 定义计算策略接口
ICalculationStrategy,包含CalculateX方法 - 为每种
DesignType实现对应的策略类,比如FooCalculationStrategy、BarCalculationStrategy - 在
CentralDesignObject中通过策略工厂,根据DesignTypeId获取对应的策略执行计算 - 此方案无需修改EF关联配置,原有实体结构保持不变,适合快速拆分膨胀的条件逻辑
核心代码示例
public interface ICalculationStrategy { decimal CalculateX(CentralDesignObject obj); } public class FooCalculationStrategy : ICalculationStrategy { public decimal CalculateX(CentralDesignObject obj) { return obj.RelatedDataA.Sum(x => x.A + x.B); } } public class BarCalculationStrategy : ICalculationStrategy { public decimal CalculateX(CentralDesignObject obj) { return obj.RelatedDataB.Average(x => x.A * x.B); } } // 策略工厂,负责根据DesignTypeId匹配对应策略 public class CalculationStrategyFactory { private readonly Dictionary<int, ICalculationStrategy> _strategies; public CalculationStrategyFactory(IEnumerable<ICalculationStrategy> strategies) { _strategies = strategies.ToDictionary( s => s switch { FooCalculationStrategy => 1, BarCalculationStrategy => 2, _ => throw new ArgumentException("未知策略类型") }, s => s); } public ICalculationStrategy GetStrategy(int designTypeId) { return _strategies.TryGetValue(designTypeId, out var strategy) ? strategy : throw new ArgumentException($"未找到DesignTypeId={designTypeId}对应的计算策略"); } } // 修改原有CentralDesignObject的计算方法 public class CentralDesignObject { // ... 原有属性保持不变 ... public decimal CalculationMethod_X(CalculationStrategyFactory factory) { var strategy = factory.GetStrategy(DesignTypeId); return strategy.CalculateX(this); } }
3. 混合模式(TPH+策略模式)
如果部分计算逻辑通用、部分差异极大,可以结合两种模式:
- 基类实现通用计算逻辑,子类仅负责差异化逻辑的调用
- 或者将复杂的差异化计算逻辑抽离为策略,在子类中注入对应的策略实例,兼顾继承的结构清晰性和策略的灵活性
是否值得改造?
- 值得改造的场景:
DesignType数量持续增加,条件分支超过3-5个,维护成本飙升- 差异化计算逻辑复杂,每个分支包含大量代码,易出错且难以测试
- 需要为不同
DesignType添加专属属性或方法,原有单一类无法满足扩展需求
- 无需改造的场景:
DesignType数量稳定(少于3个),且计算逻辑简单- 业务需求短期内不会扩展,现有代码能稳定满足需求
内容的提问来源于stack exchange,提问作者freedomdev
相关产品推荐
相关产品推荐

