使用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
相关产品推荐
相关产品推荐

