如何在LINQ中对大量相似Join操作实现DRY,避免重复编码
解决方案
完全可以通过通用方法实现,无需重复编写大量相似扩展,以下提供两种落地性强、兼容IQueryable转SQL场景的方案:
方案一:Flags枚举 + 动态表达式树(改造成本最低,灵活性最高)
适合字段固定、匹配规则经常变化的场景,无需新增方法就能支持任意忽略字段的组合。
第一步:定义忽略字段枚举
[Flags] public enum IgnoreMyFields { None = 0, F1 = 1 << 0, F2 = 1 << 1, F3 = 1 << 2, F4 = 1 << 3, F5 = 1 << 4, F6 = 1 << 5, F7 = 1 << 6, F8 = 1 << 7, F9 = 1 << 8, F10 = 1 << 9 }
第二步:实现通用Join扩展
如果使用EF Core 3.0+,可以直接用ValueTuple作为Join键,代码非常简洁:
public static class MyJoins { public static IQueryable<T> MyJoin<T, TA, TB>(this IQueryable<TA> left, IQueryable<TB> right, IgnoreMyFields ignoreFields = IgnoreMyFields.None) where TA : IMyInterface where TB : IMyInterface where T : class, IJoinInterface<TA, TB>, new() { return left.Join(right, a => BuildJoinKey(a, ignoreFields), b => BuildJoinKey(b, ignoreFields), (a, b) => new T { AA = a, BB = b }); } private static object BuildJoinKey(IMyInterface entity, IgnoreMyFields ignoreFields) { // 按忽略规则返回对应ValueTuple作为键,EF Core可正常解析为SQL的等值匹配 return ignoreFields switch { IgnoreMyFields.None => (entity.F1, entity.F2, entity.F3, entity.F4, entity.F5, entity.F6, entity.F7, entity.F8, entity.F9, entity.F10), IgnoreMyFields.F2 => (entity.F1, entity.F3, entity.F4, entity.F5, entity.F6, entity.F7, entity.F8, entity.F9, entity.F10), IgnoreMyFields.F4 | IgnoreMyFields.F7 => (entity.F1, entity.F2, entity.F3, entity.F5, entity.F6, entity.F8, entity.F9, entity.F10), // 新增组合只需加一行分支,不会出现字段匹配错误 _ => throw new NotSupportedException("暂不支持当前忽略字段组合") }; } }
如果需要兼容旧版本EF或者不想硬编码分支,可以通过动态生成表达式树的方式实现完全无硬编码的通用逻辑,无需新增分支就能支持任意组合。
使用示例
和你原来的业务逻辑完全兼容,写法更简洁:
IQueryable<TA> a = ...... IQueryable<TB> b = ...... // 全匹配 var firstPart = a.Where(MyExpression).MyJoin<ResultType, TA, TB>(b); // 忽略F2 var secondPart = a.Where(OtherExpression).MyJoin<ResultType, TA, TB>(b, IgnoreMyFields.F2); // 忽略F1、F2、F8 var thirdPart = a.Where(AnotherExpression).MyJoin<ResultType, TA, TB>(b, IgnoreMyFields.F1 | IgnoreMyFields.F2 | IgnoreMyFields.F8); var joinResult = firstPart.Concat(secondPart).Concat(thirdPart);
方案二:源码生成器(性能最优,完全零运行时开销)
如果你追求最高性能、完全兼容原有手写代码的效果,可以用C#的Source Generator在编译时自动生成所有可能的Join扩展方法。
因为只有10个字段,所有组合一共只有1024种,生成的代码和你手写的完全一致,不会有任何运行时损耗,也不会出现手写时的字段匹配错误,后续新增字段只要修改生成器逻辑就能批量更新所有方法。
内容的提问来源于stack exchange,提问作者HrkBrkkl
相关产品推荐
相关产品推荐

