利用委托与表达式树优雅批量翻译并回填实体字段

1. 背景与目标 最近项目中加入多语言翻译需求:一个实体列表里,可能有多处需要显示化 / 翻译的字段,比如枚举状态、国家名称等。一开始想用ActionFilter来实现,但是项目要用到hangfire,我们经常要用hangfire去导出excel,这里经常需要翻译,所以舍弃了这种思路,还是选择在业务代码中写入相关的翻译代码,但是写多了以后就感觉很麻烦了,于是想着再封装一个方法方便进行翻译以及赋值。 目标:以链式流式语法收集所有“待翻译文本 + 回填策略”,统一调用服务一次,最后批量回填。 2. 期望的调用方式(直觉 DSL) 最开始脑海里大概是这样设计的: var res = await users .MapTranslation(a => a.IsEnable, (a, translated) => a.IsEnableName = translated) .MapTranslation(a => a.Country, (a, translated) => a.Country = translated) .ExecuteAsync(); 语义解读: MapTranslation:收集一个“源字段 + 翻译后如何写回”的规则 ExecuteAsync():统一翻译 + 回填翻译后的内容 3. 初版实现与问题分析 下面是最初的一个版本: public static class ExpressionBuilder { public static TranslatedContext<T> BuildCollect<T>(IEnumerable<T> items) where T : class => new(items); } public class TranslatedContext<T> where T : class { private readonly IEnumerable<T> _source; private readonly List<(T item, string value, Action<T, string> action)> _translations; public TranslatedContext(IEnumerable<T> source){ _source = source; _translations=new() } public IEnumerable<T> MapTranslation(Func<T, string> selector, Action<T, string> action) { foreach (var item in _source) { var result = selector(item); _translations.Add((item, result, action)); yield return item; } } public IEnumerable<T> Execute() { foreach (var (item, enumValue, action) in _translations) action(item, enumValue); foreach (var item in _source) yield return item; } } 4. 服务注入的演进 如果把翻译Service加入流程中的话就要在 BuildCollect 里接收翻译服务: var res = await users .BuildCollect(translateService) .MapTranslation(a => a.IsEnable, (a, translated) => a.IsEnableName = translated) .ExecuteAsync(); 又觉得顺序不自然,有点反直觉了,于是思考了一下将 BuildCollect 挪进 TranslateService后,长这样: ...

2025-10-02 · 4 min · 695 words · yess