尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

AutoMapper 12.0 升级指南:等效配置覆盖规则、ResolutionContext 重构与命名约定性能优化

发布时间:2026/9/24 17:07:53

资讯中心
01
ARTICLE

AutoMapper 12.0 升级指南:等效配置覆盖规则、ResolutionContext 重构与命名约定性能优化

AutoMapper 12.0 升级指南:等效配置覆盖规则、ResolutionContext 重构与命名约定性能优化
后端【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址https://gitcode.com/gh_mirrors/au/AutoMapper点击查看免费下载导读本文以 12.0-Upgrade-Guide.md 为主线系统梳理从 12.0 版本开始 AutoMapper 引入的三类破坏性变更同图/同成员的等效配置互相覆盖规则、ResolutionContext.Options的移除与替代方案以及为性能而简化的命名约定实现。文中将结合当前仓库源码如 ResolutionContext.cs、Conventions.cs与单元测试如 ContextItems.cs、ForAllMaps.cs逐项还原变更的底层原理与迁移写法帮助你快速定位受影响的配置代码并完成升级。一、等效设置互相覆盖Equivalent settings overwrite each other12.0 明确了配置的“后写覆盖先写”语义同一张映射per map和同一个成员per member上的等效设置只能保留一份后配置的会覆盖先配置的。这条规则同时作用于 Map 运行时映射与 ProjectTo 投影查询理解它能避免升级后出现“配置莫名失效”的困惑。1.1 规则粒度每张映射一个类型转换器每个成员一个解析器每张 map 只能有一个类型转换器type converterConvertUsing/ConvertUsingITypeConverter这类针对整个 Source→Destination 类型的转换配置同一张 map 内重复设置时后者覆盖前者。每个 member 只能有一个解析器resolverMapFrom、MapFromTValueResolver、ResolveUsing等针对单个成员的取值配置后设置者覆盖先设置者。1.2 关键陷阱等价配置并不总是显而易见官方指南特别强调很多配置表面上不同、实质上是“等价”的例如值转换器value converter本质上是解析器resolver的一种特化因此同一成员上后写的ConvertUsing会覆盖先写的MapFrom。也就是说下面这段代码最终生效的是ConvertUsingvar config new MapperConfiguration(cfg { cfg.CreateMapSource, Dest() .ForMember(d d.Value, opt opt.MapFrom(src src.OtherValue)) // 先写被覆盖 .ForMember(d d.Value, opt opt.ConvertUsing(new SomeConverter())); // 后写生效 });同理同一成员上先写MapFromTResolver再写MapFrom(expr)后者也会生效。由于覆盖顺序就是代码书写顺序迁移时可优先检查链式配置中同一成员的多次.ForMember调用。1.3 不能为Map和ProjectTo分别配置12.0 起同一张 map / 同一个成员上不能分别为对象映射Map和投影查询ProjectTo保存两套独立配置。映射配置在 AutoMapper 中是共享的一张TypeMap/ 一个PropertyMap同时驱动运行时表达式与查询投影见 ProjectionBuilder.cs。如果你之前通过不同分支为Map与ProjectTo设置了不同行为升级后必须合并为同一套配置或改用条件表达式在单一配置内区分场景。1.4ForAllMaps/ForAllPropertyMaps的覆盖场景ForAllMaps所有 map 生效与ForAllPropertyMaps满足条件的成员生效是批量配置入口。由于它们在实际创建 map 时执行可能覆盖某张具体 map 里已经单独设置的内容。这是文档明确提示的“另一种可能发生覆盖的场景”。参考 ForAllMaps.cs 中的测试对多张 map 统一调用ForMember(Number, o o.MapFromMinusOneResolver())最终三张 map 的Number全部被批量解析器接管var config new MapperConfiguration(cfg { cfg.CreateMapSource, Destination(); cfg.CreateMapSource1, Destination1(); cfg.CreateMapSource2, Destination2(); cfg.ForAllMaps((tm, map) map.ForMember(Number, o o.MapFromMinusOneResolver())); }); // 三张 map 的 Number 均被映射为 -1批量配置同样适用于构造参数与值转换器。例如 ValueConverters.cs 中通过ForAllPropertyMaps对“源类型为int、目标类型为string”的所有成员统一应用值转换器cfg.ForAllPropertyMaps( pm pm.SourceType typeof(int) pm.DestinationType typeof(string), (pm, opt) opt.ConvertUsing(new EightDigitIntToStringConverter()));因此升级时若发现“某个成员的单独配置没生效”应从三个方面排查同成员链式配置的后写覆盖、Map/ProjectTo共享配置冲突、以及ForAllMaps/ForAllPropertyMaps批量覆盖。相关 API 的定义可查阅 Profile.cs 与 AutoMapperSpecBase.cs。二、ResolutionContext.Options已移除2.1 变更内容12.0 移除了ResolutionContext.Options属性以及通过它暴露的ServiceCtor等访问方式。过去在自定义 resolver / type converter / value converter 中通过context.Options读取映射参数与构造服务的代码升级后需要改写。从 ResolutionContext.cs 可以看到 12.x 当前的公开上下文形态ResolutionContext.Items返回Dictionarystring, object即Map调用时通过 options 传入的上下文项ResolutionContext.TryGetItems(out items)12.0.1 新增用于安全探测是否传入了上下文未传入时返回false而不是抛异常ResolutionContext.State在Map调用中传入的单个状态对象与Items在同一Map调用中互斥详见 IMappingOperationOptions.cs 中两个属性的注释ResolutionContext.Mapper用于在自定义逻辑中继续发起嵌套映射。2.2 使用Items传递上下文数据在Map调用侧传入上下文项var dest mapper.MapSource, Dest(source, opt { opt.Items[Item] 10; });在 resolver / converter 侧读取参考 ContextItems.cs 的When_mapping_with_contextual_values测试public class ContextResolver : IMemberValueResolverSource, Dest, int, int { public int Resolve(Source src, Dest d, int source, int dest, ResolutionContext context) source (int)context.Items[Item]; }需要注意的是Items的取值逻辑要求Map调用必须使用带ActionIMappingOperationOptions的重载。如果用了不带 options 的重载直接访问context.Items会抛出InvalidOperationException异常信息原文为见 ResolutionContext.csContext.Items are only available when using a Map overload that takes ActionIMappingOperationOptions! Consider using Context.TryGetItems instead.对应的负向测试When_mapping_with_contextual_values_wrong_overloadContextItems.cs验证了这一行为。2.3 用TryGetItems探测上下文12.0.1当同一个 resolver / converter 既要兼容“不传上下文”的Map调用、又要支持传入覆盖值时使用TryGetItems最稳妥public override ExpressionFuncint, int ProjectToExpression() throw new NotSupportedException(); // 类型转换器内部 c c.TryGetItems(out var items) ? (int)items[override] : s测试用例 Context_try_get_items 验证了两种调用均不抛异常Mapint(42).ShouldBe(42); // 未传上下文取源值 Mapper.Mapint(42, o o.Items[override] 43).ShouldBe(43); // 传入上下文取覆盖值2.4State单值状态的替代方案如果你只需要传入一个对象而不是键值集合可以用State。它与Items在同一Map调用中互斥测试 When_mapping_with_context_state 展示了完整用法var dest config.CreateMapper().MapSource, Dest( new Source { Value 5 }, opt { opt.State 10; }); // State 传入单个对象 // resolver 中读取 public int Resolve(Source src, Dest d, int source, int dest, ResolutionContext context) source (int)context.State;从实现上看IMappingOperationOptions.csMappingOperationOptions.Items实际上是惰性把State初始化为字典后复用同一存储这也解释了二者“互斥”的语义一次Map调用只能以一种形式携带上下文。2.5ServiceCtor改用依赖注入或在Map调用中传入文档明确建议放弃通过ResolutionContext访问ServiceCtor的做法改用依赖注入或在Map调用中显式传入所需对象。当前实现中 ResolutionContext.cs 的ServiceCtor()仅保留为内部兜底_options?.ServiceCtor ?? _mapper.ServiceCtor用于CreateInstance创建目标实例不再作为面向使用者的公共上下文 API。若个别场景仍需要在单次映射中注入服务可借助IMappingOperationOptions.ConstructServicesUsing子容器场景见 IMappingOperationOptions.csmapper.MapSource, Dest(source, opt opt.ConstructServicesUsing(type myChildContainer.Resolve(type)));更常规的做法是在MapperConfiguration构造时通过ConstructServicesUsing注册服务提供方或让 resolver / converter 直接使用项目自身的 DI 容器。相关实践可进一步参考 Dependency-injection.md。三、命名约定为性能而简化的实现3.1 变更动机12.0 出于性能考虑简化了命名约定的匹配实现尤其影响扁平化映射的名称拆分逻辑。官方立场是与其处理每一种命名怪癖不如提供一个简单快速、覆盖大多数场景的实现如果你的场景无法被默认实现覆盖可以编写自定义命名约定。3.2 源码中的实现分层从 Conventions.cs 可以看到命名匹配的当前结构MemberConfiguration保存SourceNamingConvention、DestinationNamingConvention以及NameToMemberMappers列表MemberConfiguration.Seal()Conventions.cs当源、目标命名约定都是默认的PascalCaseNamingConvention时走专门的DefaultNameSplitMember快速路径否则走通用的ConventionsNameSplitMember。这就是“简化 性能优化”的核心所在——默认情形下不再经过通用的约定拆分流程NameSplitMember抽象基类及其两个子类DefaultNameSplitMember直接按大写字母边界切分并递归匹配ConventionsNameSplitMember则先按DestinationNamingConvention.Split切分、再按SourceNamingConvention.SeparatorCharacter重组后逐段匹配反向映射时二者互换见 Conventions.cs。INamingConvention接口INamingConvention.cs只包含两个成员自定义成本很低public interface INamingConvention { string[] Split(string input); string SeparatorCharacter { get; } }仓库内置三种约定实现实现SeparatorCharacterSplit行为用途PascalCaseNamingConvention按大写字母边界拆分含缩写/连续大写处理默认约定支持InnerSourceValue这类扁平化名称LowerUnderscoreNamingConvention_按下划线拆分支持ja_se_zovem_imenom这类 snake_case 名称ExactMatchNamingConventionnull不拆分返回空数组仅精确匹配用于关闭扁平化拆分3.3 自定义命名约定的写法在全局配置中覆盖MapperConfigurationExpression/Profile均暴露SourceMemberNamingConvention与DestinationMemberNamingConvention见 IProfileExpression.cs 与 Profile.cspublic sealed class KebabCaseNamingConvention : INamingConvention { public string SeparatorCharacter -; public string[] Split(string input) input.Split(-, StringSplitOptions.RemoveEmptyEntries); } var config new MapperConfiguration(cfg { cfg.SourceMemberNamingConvention new KebabCaseNamingConvention(); cfg.DestinationMemberNamingConvention new KebabCaseNamingConvention(); });如果需要彻底关闭名称拆分只做精确匹配把两侧约定都设为ExactMatchNamingConvention.Instance即可。测试 NamingConventions.cs 验证了关闭后DestinationMemberNamingConvention ExactMatchNamingConvention.Instance时COMPANY_Name这类名称将不再被自动匹配而进入未映射校验错误。反向映射Reverse Map同样遵循命名约定测试When_mapping_with_lowercase_naming_conventions_two_waysNamingConventions.cs演示了 snake_case ↔ PascalCase 双向往返映射——正向用SourceMemberNamingConvention LowerUnderscoreNamingConvention反向用DestinationMemberNamingConvention LowerUnderscoreNamingConvention。此外若你的命名差异属于前缀/后缀/字符串替换类如RecognizePrefixes、RecognizePostfixes、ReplaceMemberName这些能力仍由PrePostfixName与ReplaceName两个ISourceToDestinationNameMapper承担见 Conventions.cs与 12.0 的命名约定简化互不冲突。相关完整说明可参考 Configuration.md。四、升级迁移清单结合上文升级到 AutoMapper 12.x 时建议按以下顺序自查检查等效配置覆盖搜索同一 map 中重复的ConvertUsing/MapFrom/ResolveUsing链式调用确认最终生效项符合预期确认没有为Map与ProjectTo分别维护两套配置留意ForAllMaps/ForAllPropertyMaps对单张 map 配置的批量覆盖。替换ResolutionContext.Options将读取上下文项的代码改为context.Items若需要兼容“未传上下文”的调用使用 12.0.1 起的context.TryGetItems需要传单个对象时改用State与Items互斥。移除ServiceCtor依赖改用依赖注入或在Map调用中通过ConstructServicesUsing/Items/State显式传入所需对象。验证命名约定行为运行AssertConfigurationIsValid校验未映射成员若默认的 PascalCase 快速路径无法满足命名习惯按INamingConvention编写自定义约定全局或按 Profile 设置或直接使用ExactMatchNamingConvention.Instance关闭拆分。五、延伸阅读Custom-type-converters.md每张 map 的类型转换器配置Custom-value-resolvers.md每成员解析器与MapFromTResolver用法Value-converters.md值转换器与解析器之间的关系Reverse-Mapping-and-Unflattening.md反向映射中的命名约定与ForPathQueryable-Extensions.mdProjectTo与 Map 共享配置的说明Dependency-injection.mdConstructServicesUsing与 DI 集成13.0-Upgrade-Guide.md12.x 之后的后续版本升级指引赞分享后端【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址https://gitcode.com/gh_mirrors/au/AutoMapper点击查看免费下载相关推荐Rome 命名规范 Lint 规则 useNamingConvention 完整指南覆盖全代码库的命名约定检查与自动修复Rome 命名规范 Lint 规则 useNamingConvention 完整指南覆盖全代码库的命名约定检查与自动修复 本文全面解读 Rome该项目仓库开发工具CLILint格式化静态分析代码质量构建工具Apollo配置优先级多层配置覆盖规则详解Apollo配置优先级多层配置覆盖规则详解 在分布式系统中配置管理的复杂性往往来源于多环境、多集群、多应用间的配置协同。Apollo配置中心Apollo配置中心后端微服务PARD-Qwen3-0.6B核心原理深度解析并行草稿模型适配技术揭秘PARD Qwen3 0.6B核心原理深度解析并行草稿模型适配技术揭秘 想要了解如何通过并行草稿模型技术将大语言模型推理速度提升3倍以上吗PARD Qwen上一篇如何免费将网易云 NCM 文件转换为 MP3ncmdump 新手完整指南下一篇QQ空间备份怎么免费做说说、相册、日志一次导出的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。