You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将带条件逻辑的多功能类拆分为特定逻辑的基础类(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:16:13