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

如何在规约模式(Specification Pattern)泛型中支持类继承特性?

解决方案

方案1:添加基类规约向下适配能力(侵入性最低,无需修改现有规约)

核心思路是通过表达式参数替换,将针对基类的规约包装为适用于子类的规约,完全兼容EF表达式解析逻辑。

首先添加参数替换访问器(如果现有代码已有可跳过):

internal class ParameterReplaceVisitor : ExpressionVisitor
{
    private readonly ParameterExpression _oldParam;
    private readonly ParameterExpression _newParam;

    public ParameterReplaceVisitor(ParameterExpression oldParam, ParameterExpression newParam)
    {
        _oldParam = oldParam;
        _newParam = newParam;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node == _oldParam ? _newParam : base.VisitParameter(node);
    }
}

添加适配用的规约包装类:

internal class AdaptedSpecification<TDerived, TBase> : Specification<TDerived>
    where TDerived : TBase
{
    private readonly Specification<TBase> _baseSpec;

    public AdaptedSpecification(Specification<TBase> baseSpec)
    {
        _baseSpec = baseSpec;
    }

    public override Expression<Func<TDerived, bool>> FilterExpr()
    {
        var baseExpr = _baseSpec.FilterExpr();
        var derivedParam = Expression.Parameter(typeof(TDerived), baseExpr.Parameters[0].Name);
        var visitor = new ParameterReplaceVisitor(baseExpr.Parameters[0], derivedParam);
        var newBody = visitor.Visit(baseExpr.Body);
        return Expression.Lambda<Func<TDerived, bool>>(newBody, derivedParam);
    }
}

添加规约扩展方法简化调用:

public static class SpecificationExtensions
{
    public static Specification<TDerived> As<TDerived, TBase>(this Specification<TBase> spec) 
        where TDerived : TBase
    {
        return new AdaptedSpecification<TDerived, TBase>(spec);
    }

    // 重载And方法,无需显式转换即可直接组合父子类规约
    public static Specification<TDerived> And<TDerived, TBase>(this Specification<TBase> left, Specification<TDerived> right)
        where TDerived : TBase
    {
        return left.As<TDerived, TBase>().And(right);
    }
}

使用方式:

// 方式1:显式转换后组合
var johnSpec = new NameSpec("John").As<Baby, Person>();
var combinedSpecification = johnSpec.And(new IsAbleToTalkSpec());

// 方式2:直接用重载的And方法,自动适配
var combinedSpecification = new NameSpec("John").And(new IsAbleToTalkSpec());

List<Baby> result = myRepository.Find(combinedSpecification); 

方案2:通用泛型规约(更适合大量复用基类属性规约的场景)

将针对基类属性的规约定义为带约束的泛型类,所有子类可直接共用,无需重复实现:

// 改造后的NameSpec,支持所有Person子类使用
public class NameSpec<T> : Specification<T> where T : Person
{
    private readonly string _name;

    public NameSpec(string name)
    {
        _name = name;
    }

    public override Expression<Func<T, bool>> FilterExpr()
    {
        return p => p.Name == _name;
    }
}

使用方式:

var johnSpec = new NameSpec<Baby>("John");
var combinedSpecification = johnSpec.And(new IsAbleToTalkSpec());
List<Baby> result = myRepository.Find(combinedSpecification);

方案选择建议

  • 若已有大量写好的非泛型基类规约,不想修改原有代码,选方案1,侵入性最低
  • 若还在规约设计初期,基类属性需要大量给子类复用,选方案2,代码更简洁易维护

两种方案生成的表达式都可被EF正常解析为SQL,不会出现内存过滤的问题。

内容的提问来源于stack exchange,提问作者gst

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:15:01