AutoMapper 嵌套映射(Nested Mappings)完全指南:原理、配置与源码剖析
后端【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址https://gitcode.com/gh_mirrors/au/AutoMapper点击查看免费下载导读嵌套映射Nested Mappings是 AutoMapper 在对象映射时解决复杂类型成员的核心机制当源类型的一个属性本身是另一个复杂类型时映射引擎会查找并复用该源/目标类型对已注册的 TypeMap 来完成递归映射。通过本指南你将掌握嵌套映射的完整配置方法、与扁平化Flattening的取舍、配置验证方式以及从 TypeMap.cs 到 MapperConfiguration.cs 的底层解析原理从而在 .NET 项目中自如构建多层级的 DTO 模型。什么是嵌套映射AutoMapper 在执行映射时会使用多种方法来解析目标成员的取值。其中一种方法就是使用另一个类型映射type map当源成员类型与目标成员类型在映射配置中同样被注册为源/目标类型对时AutoMapper 会自动递归地调用这层映射来填充目标成员。正如 Nested-mappings.md 所描述的借助嵌套映射我们不仅可以扁平化源类型还能构建出同样复杂的、与源结构对应的目标类型。这在 API 响应模型、领域模型向视图模型转换等场景中非常常见目标 DTO 保留了与领域对象一致的分层结构。例如源类型包含另一个复杂类型public class OuterSource { public int Value { get; set; } public InnerSource Inner { get; set; } } public class InnerSource { public int OtherValue { get; set; } }我们当然可以把OuterSource.Inner.OtherValue扁平化为目标上的一个InnerOtherValue属性见 Flattening.md 的成员名分词匹配机制但有些场景下我们希望目标模型同样保有一个对应的复杂类型public class OuterDest { public int Value { get; set; } public InnerDest Inner { get; set; } } public class InnerDest { public int OtherValue { get; set; } }此时除了注册外层类型对还需要显式注册内层的源/目标类型映射。配置嵌套映射的完整示例var config new MapperConfiguration(cfg { cfg.CreateMapOuterSource, OuterDest(); cfg.CreateMapInnerSource, InnerDest(); }, loggerFactory); config.AssertConfigurationIsValid(); var source new OuterSource { Value 5, Inner new InnerSource { OtherValue 15 } }; var mapper config.CreateMapper(); var dest mapper.MapOuterSource, OuterDest(source); dest.Value.ShouldEqual(5); dest.Inner.ShouldNotBeNull(); dest.Inner.OtherValue.ShouldEqual(15);说明new MapperConfiguration(cfg {...}, loggerFactory)中的loggerFactory为 Microsoft.Extensions.Logging 的日志工厂参数是 AutoMapper 依赖注入支持的一部分可参考 Dependency-injection.md在不使用日志的场景下可省略该参数直接写new MapperConfiguration(cfg {...})。从上面的示例可以提炼出嵌套映射的两个关键事实原文档明确指出类型配置的顺序无关紧要先注册OuterSource - OuterDest还是先注册InnerSource - InnerDest都不影响结果AutoMapper 会在需要时解析到任意已注册的类型映射调用Map时无需指定任何内部类型映射只需传入最外层要使用的类型映射内层的InnerSource - InnerDest会被映射引擎自动发现并执行。断言验证AssertConfigurationIsValid示例中config.AssertConfigurationIsValid()用于在启动阶段验证所有已注册的映射配置是否完整。嵌套映射下这尤为重要如果外层映射已注册、而内层InnerSource - InnerDest缺失验证会失败并抛出AutoMapperConfigurationException提示缺少对应的类型映射。这能在开发早期暴露遗漏的内层映射而不是等到运行时才抛出InvalidOperationException该异常信息在 TypeMap.cs 中定义为Missing map from ... to ...。嵌套映射的底层原理源码视角嵌套映射之所以只需配置、无需显式指定其背后是 AutoMapper 在映射表达式构建阶段对成员类型对的自动解析。1. 成员匹配与属性映射收集在 TypeMap.cs 中TypeMap构造函数遍历目标类型的可写成员DestinationTypeDetails.WriteAccessors通过profile.MapDestinationPropertyToSource(...)把源成员解析结果收集为PropertyMap。当一个目标成员的类型是复杂类型时这个成员本身就会在后续执行阶段触发一次独立的类型映射解析。2. 运行时解析 TypeMap在 MapperConfiguration.cs 的ResolveTypeMap(TypePair typePair)中AutoMapper 会先查询已解析映射缓存_resolvedMaps未命中时通过GetTypeMap进行查找首先尝试查找已注册的泛型映射FindClosedGenericTypeMapFor然后遍历源类型与目标类型的继承链与接口GetTypeInheritance见 MapperConfiguration.cs尝试找到最匹配的已配置映射找到 TypeMap 后若其MapExpression为空则在加锁状态下调用typeMap.Seal(this)完成映射表达式的封口编译并在注释中特别说明动态创建的类型映射需要在GetTypeMap外部封口以处理递归见 MapperConfiguration.cs——这正是嵌套以及循环引用映射能够安全递归构建的关键所在。3. 成员映射表达式的生成在 ExpressionBuilder.cs 的MapExpression扩展方法中对每个需要映射的成员先ResolveTypeMap获取内层 TypeMap若存在该 TypeMap则调用typeMap.Invoke(source, destination)生成调用表达式若不存在例如目标成员只是string、集合等基础能力则回退到configuration.FindMapper(typePair)查找内置 Mapper如 AssignableMapper.cs、CollectionMapper.cs 等生成对应的映射表达式最后通过NullCheckSource见 ExpressionBuilder.cs为引用类型源成员包裹判空逻辑源为null时返回目标默认值或清空目标集合避免NullReferenceException。4. 执行计划构建最终TypeMapPlanBuilder.cs 将上述表达式组装为一个完整的委托mapper func嵌套成员在这里以_configuration.MapExpression(...)的形式被内联进执行计划见 TypeMapPlanBuilder.cs。这意味着嵌套映射与顶层映射是同一个编译期执行计划的一部分运行时开销极低。嵌套映射 vs 扁平化如何选择维度扁平化Flattening嵌套映射Nested Mappings目标结构打平为单一层级 DTO保留与源对应的层级结构是否需要内层映射不需要依赖成员名分词匹配如Customer.Name→CustomerName、GetTotal()→Total需要为每个内层类型对注册映射典型场景列表项、报表、轻量视图模型API DTO、层级复杂的聚合根原文档的结论是同时掌握扁平化与嵌套映射可以塑造出满足各种需求的多种目标形态。当内层结构需要被多个外层复用、或目标端明确要求分层时嵌套映射是更清晰的选择而当目标模型只需要少量扁平字段时扁平化更省配置。嵌套映射的变体集合与投影嵌套映射不仅适用于单个对象属性同样适用于集合嵌套——例如源对象有一个ListInnerSource属性目标对应ListInnerDest。此时 AutoMapper 会先为元素类型对内联执行InnerSource - InnerDest映射再交由 CollectionMapper.cs 完成集合的创建与填充该文件第 243 行正是通过configuration.FindTypeMapFor(sourceType, destinationType) ! null判断是否走类型映射路径。在 LINQ 投影场景下ProjectTo嵌套映射同样生效且会被翻译为投影表达式而不是逐条执行。仓库测试 NestedAndArraysTests.cs 中的SubMap测试演示了这一模式var config new MapperConfiguration(cfg { cfg.CreateProjectionSubEntity, SubEntityViewModel() .ForMember(m m.Description, o o.MapFrom(s s.Description)); cfg.CreateProjectionEntity, EntityDetailledViewModel(); // SubEntities 集合嵌套映射 });这里Entity - EntityDetailledViewModel映射中的SubEntities集合成员会自动解析SubEntity - SubEntityViewModel的类型映射并生成可在查询提供程序如 EF Core中翻译的表达式从而实现数据库端的嵌套投影详见 Queryable-Extensions.md 与 Projection.md。嵌套映射与 IncludeMembers 的区别如果需要把子对象的成员平铺到目标对象、同时又想复用子类型的映射配置可以使用IncludeMembers详见 Flattening.md 和测试 IncludeMembers.cs。它与嵌套映射的本质区别在于嵌套映射目标对象保有与源对应的嵌套结构内层成员通过另一个 TypeMap 递归填充IncludeMembers目标对象是扁平结构但复用了子类型到目标类型的映射配置采用组合而非继承的方式其工作方式与映射继承Mapping-inheritance.md类似。两者并不冲突当一个目标模型既包含平铺字段又包含复杂结构时可以组合使用。常见问题与最佳实践忘记注册内层映射嵌套映射要求内层类型对必须显式注册。务必调用AssertConfigurationIsValid()在启动时校验避免运行时才暴露缺失映射。深层嵌套导致栈溢出若对象图存在循环引用A 包含 B、B 又包含 A需要为相关类型映射配置MaxDepth或PreserveReferences来控制递归深度参见仓库测试 DeepNestingStackOverflow.cs。内层映射的定制内层映射同样支持ForMember、Ignore、条件映射等全部配置能力——因为它在本质上就是一个独立的 TypeMap遵循 Configuration.md 中描述的统一配置模型。配置顺序无关性不要依赖注册顺序AutoMapper 的 TypeMap 解析是基于类型对查找的_configuredMaps字典与注册先后无关这也是 MapperConfiguration.cs 中FindTypeMapFor直接按TypePair取值的直接结果。小结嵌套映射是 AutoMapper 构建复杂目标模型的核心能力通过为每个内层类型对注册独立的 TypeMap映射引擎会在构建执行计划时自动递归解析并内联内层映射让开发者无需关心调用顺序与内部细节。结合扁平化、IncludeMembers、集合映射与ProjectTo投影你可以用同一套声明式配置灵活塑造出任意复杂度的目标对象形态。文中涉及的源码与测试TypeMap.cs、MapperConfiguration.cs、ExpressionBuilder.cs、NestedAndArraysTests.cs可作为继续深入研究的起点。赞分享后端【免费下载链接】AutoMapperA convention-based object-object mapper in .NET.项目地址https://gitcode.com/gh_mirrors/au/AutoMapper点击查看免费下载相关推荐three.js TileShadowNode 平铺阴影映射完全指南原理、配置与源码剖析three.js TileShadowNode 平铺阴影映射完全指南原理、配置与源码剖析 本文围绕 three.js 中用于实现 平铺阴影映射Tiled S前端3D渲染图形学Slint SC 安全手册站点构建全指南Astro Starlight 文档管线与 slint-doc-generator 源码解析Slint SC 安全手册站点构建全指南Astro Starlight 文档管线与 slint doc generator 源码解析 Slint 开源仓库中除后端Vue Router 嵌套路由Nested Routes完全指南children 配置、router-view 出口与源码级匹配原理Vue Router 嵌套路由Nested Routes完全指南children 配置、 router view 出口与源码级匹配原理 嵌套路由是构建真前端路由上一篇Cursor Free VIP破解工具5步解锁AI编程助手Pro功能的完整指南下一篇BG3 Mod Manager轻松管理《博德之门3》模组的高效工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考