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

使用Mediator时对C# API控制器进行版本控制的方案咨询

C# API版本控制中Mediator命令/请求的版本化方案选择

针对你提到的Mediator命令/请求版本化问题,两种方案各有优劣,下面具体分析并给出建议:

方案1:传递版本号并实现分支逻辑

在请求类中加入版本标识,在Handler内部根据版本号做逻辑分支:

// 请求类
public class GetInfoRequest : IRequest<InfoResult>
{
    public string ApiVersion { get; set; }
}

// Handler实现
public class GetInfoRequestHandler : IRequestHandler<GetInfoRequest, InfoResult>
{
    public async Task<InfoResult> Handle(GetInfoRequest request, CancellationToken cancellationToken)
    {
        if (request.ApiVersion == "1.0")
        {
            // 版本1.0的业务逻辑
            return new InfoResultV1();
        }
        else if (request.ApiVersion == "2.0")
        {
            // 版本2.0的业务逻辑
            return new InfoResultV2();
        }
        throw new NotSupportedException("不支持的API版本");
    }
}

// 控制器调用
[ApiVersion("1.0")]
[HttpGet]
public async Task<IActionResult> Get()
{
    var result = await Mediator.Send(new GetInfoRequest { ApiVersion = "1.0" });
    return Ok(result);
}

[ApiVersion("2.0")]
[HttpGet]
public async Task<IActionResult> Get()
{
    var result = await Mediator.Send(new GetInfoRequest { ApiVersion = "2.0" });
    return Ok(result);
}

优缺点

  • 优点:无需创建大量重复的请求/Handler类,减少代码冗余;版本逻辑集中在Handler内,便于统一查看。
  • 缺点:随着版本迭代,Handler会逐渐臃肿,分支逻辑复杂后可读性、可维护性下降;不同版本逻辑耦合,修改时容易引发旧版本的回归问题。

方案2:复制受影响的Mediator请求/命令类

为每个版本创建独立的请求类和对应的Handler,完全隔离不同版本的逻辑:

// V1版本
public class GetInfoRequest : IRequest<InfoResultV1> { }
public class GetInfoRequestHandler : IRequestHandler<GetInfoRequest, InfoResultV1>
{
    public async Task<InfoResultV1> Handle(GetInfoRequest request, CancellationToken cancellationToken)
    {
        // 版本1.0的独立逻辑
        return new InfoResultV1();
    }
}

// V2版本
public class GetInfoRequestV2 : IRequest<InfoResultV2> { }
public class GetInfoRequestV2Handler : IRequestHandler<GetInfoRequestV2, InfoResultV2>
{
    public async Task<InfoResultV2> Handle(GetInfoRequestV2 request, CancellationToken cancellationToken)
    {
        // 版本2.0的独立逻辑
        return new InfoResultV2();
    }
}

// 控制器调用
[ApiVersion("1.0")]
[HttpGet]
public async Task<IActionResult> Get()
{
    var result = await Mediator.Send(new GetInfoRequest());
    return Ok(result);
}

[ApiVersion("2.0")]
[HttpGet]
public async Task<IActionResult> Get()
{
    var result = await Mediator.Send(new GetInfoRequestV2());
    return Ok(result);
}

优缺点

  • 优点:不同版本逻辑完全隔离,互不影响,大幅降低回归风险;每个Handler职责单一,代码清晰,便于单独测试和维护;废弃旧版本时可直接删除对应类,无额外清理成本。
  • 缺点:若版本差异较小,会产生一定的代码冗余;需要手动同步共享逻辑(可通过抽象基类/公共服务解决)。

方案选择建议

如果是破坏性变更(比如返回结构完全重构、核心逻辑彻底调整),优先选方案2——破坏性变更意味着版本间逻辑差异大,隔离维护能避免耦合带来的潜在风险。

如果是兼容型小更新(比如新增字段、微调逻辑但不影响原有功能),可以考虑方案1,减少不必要的代码重复。

即使选择方案2,也可以通过抽象基类复用底层逻辑,减少冗余:

public abstract class BaseInfoHandler<TResult>
{
    // 共享的底层逻辑,比如数据查询
    protected async Task<RawData> FetchRawData()
    {
        // 通用数据获取逻辑
    }
}

public class GetInfoRequestHandler : BaseInfoHandler<InfoResultV1>, IRequestHandler<GetInfoRequest, InfoResultV1>
{
    public async Task<InfoResultV1> Handle(GetInfoRequest request, CancellationToken cancellationToken)
    {
        var rawData = await FetchRawData();
        // V1特有的转换逻辑
        return new InfoResultV1();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:44:56