C#中如何基于输入参数用LINQ查询不同DataSet替代switch case
方案说明
完全可以绕过switch case实现需求,根据项目复杂度和后续扩展需求,可以选择以下两种实现方式:
轻量实现(适合分类少、变动不频繁的场景)
不需要额外定义抽象和独立类,直接在业务类初始化时构建分类与查询委托的映射字典,调用时按传入分类取对应委托执行即可,代码如下:
public class YourBusinessService { private readonly YourDbContext _data; private readonly IMapper _mapper; private readonly IReadOnlyDictionary<string, Func<int, CancellationToken, Task<RequestOutputModel>>> _queryHandlers; // 构造函数注入依赖,同时初始化查询映射字典 public YourBusinessService(YourDbContext data, IMapper mapper) { _data = data; _mapper = mapper; _queryHandlers = new Dictionary<string, Func<int, CancellationToken, Task<RequestOutputModel>>>(StringComparer.OrdinalIgnoreCase) { ["RequestTypeOne"] = async (id, ct) => await _mapper .ProjectTo<RequestTypeOneOutputModel>(_data.RequestTypeOne.Where(e => e.Id == id)) .FirstOrDefaultAsync(ct), ["RequestTypeTwo"] = async (id, ct) => await _mapper .ProjectTo<RequestTypeTwoOutputModel>(_data.RequestTypeTwo.Where(e => e.Id == id)) .FirstOrDefaultAsync(ct) // 后续新增分类只需要在这里加一行映射即可 }; } public async Task<RequestOutputModel> SearchById(int id, string category, CancellationToken cancellationToken) { if (!_queryHandlers.TryGetValue(category, out var handler)) { // 可替换为项目内自定义的业务异常 throw new ArgumentException($"不支持的分类类型: {category}", nameof(category)); } return await handler(id, cancellationToken); } }
可扩展实现(适合分类多、后续迭代频繁的场景)
基于策略模式实现,符合开闭原则,后续新增分类不需要修改核心查询逻辑:
- 首先定义统一的查询策略接口,约束所有分类查询的标准
/// <summary> /// 分类查询策略接口 /// </summary> public interface ICategoryQueryStrategy { /// <summary> /// 当前策略支持的分类标识 /// </summary> string CategoryName { get; } /// <summary> /// 按Id执行查询 /// </summary> Task<RequestOutputModel> QueryByIdAsync(int id, CancellationToken cancellationToken); }
- 为每个分类实现独立的策略类,以RequestTypeOne为例:
public class RequestTypeOneQueryStrategy : ICategoryQueryStrategy { private readonly YourDbContext _data; private readonly IMapper _mapper; public RequestTypeOneQueryStrategy(YourDbContext data, IMapper mapper) { _data = data; _mapper = mapper; } public string CategoryName => "RequestTypeOne"; public async Task<RequestOutputModel> QueryByIdAsync(int id, CancellationToken cancellationToken) { return await _mapper .ProjectTo<RequestTypeOneOutputModel>(_data.RequestTypeOne.Where(e => e.Id == id)) .FirstOrDefaultAsync(cancellationToken); } }
RequestTypeTwo的实现逻辑与上述一致,仅需替换对应的DbSet和输出模型类型即可。
3. 注册策略与分发器,在服务配置阶段完成所有策略的自动扫描和映射:
// Program.cs 服务注册代码 // 自动扫描程序集中所有策略实现并注册 builder.Services.Scan(scan => scan .FromAssemblyOf<ICategoryQueryStrategy>() .AddClasses(classes => classes.AssignableTo<ICategoryQueryStrategy>()) .AsImplementedInterfaces() .WithScopedLifetime()); // 注册查询分发器 builder.Services.AddScoped<CategoryQueryDispatcher>(sp => { var strategies = sp.GetRequiredService<IEnumerable<ICategoryQueryStrategy>>(); var strategyDict = strategies.ToDictionary(s => s.CategoryName, StringComparer.OrdinalIgnoreCase); return new CategoryQueryDispatcher(strategyDict); });
分发器的核心逻辑很简单,负责根据传入分类匹配对应策略执行:
public class CategoryQueryDispatcher { private readonly IReadOnlyDictionary<string, ICategoryQueryStrategy> _strategyDict; public CategoryQueryDispatcher(IReadOnlyDictionary<string, ICategoryQueryStrategy> strategyDict) { _strategyDict = strategyDict; } public async Task<RequestOutputModel> SearchById(int id, string category, CancellationToken cancellationToken) { if (!_strategyDict.TryGetValue(category, out var strategy)) { throw new ArgumentException($"不支持的分类类型: {category}", nameof(category)); } return await strategy.QueryByIdAsync(id, cancellationToken); } }
- 最终业务类只需要注入分发器即可直接调用,不需要写任何分支判断:
public class YourBusinessService { private readonly CategoryQueryDispatcher _queryDispatcher; public YourBusinessService(CategoryQueryDispatcher queryDispatcher) { _queryDispatcher = queryDispatcher; } public Task<RequestOutputModel> SearchById(int id, string category, CancellationToken cancellationToken) { return _queryDispatcher.SearchById(id, category, cancellationToken); } }
方案优势
- 所有分支逻辑统一管理,不会出现switch分支过长导致的代码可读性差、维护成本高的问题
- 轻量实现无额外抽象,开发成本低,适配简单业务场景
- 策略模式实现下单个分类的查询逻辑独立拆分,职责单一,方便编写单元测试,新增分类时不会侵入原有业务代码
- 字典匹配默认配置大小写不敏感,避免传参格式问题导致的匹配异常
内容的提问来源于stack exchange,提问作者Martin Zhechev
相关产品推荐
相关产品推荐

