研究AutoMapper源码前,我们先来看一下AutoMapper的作用


官网解释:AutoMapper是一个简单的小程序库,旨在解决看似复杂的问题-摆脱将一个对象映射到另一个对象的代码 解释

首先一个简单的使用AutoMapper方法演示

ar config = new MapperConfiguration(cfg => cfg.CreateMap<ModelObject, ModelDto>());var mapper1 = config.CreateMapper();var mode;= mapper1.Map<ModelObject>(new ModelDto{ Name= 1 });

构造函数

在这段代码中默认创建MapperConfiguration对象,并且传入一个带有映射关系的Action
当MapperConfiguration创建时,会默认执行构造函数

 public MapperConfiguration(MapperConfigurationExpression configurationExpression){_mappers = configurationExpression.Mappers.ToArray();_resolvedMaps = new LockingConcurrentDictionary<TypePair, TypeMap>(GetTypeMap);_executionPlans = new LockingConcurrentDictionary<MapRequest, Delegate>(CompileExecutionPlan);_validator = new ConfigurationValidator(this, configurationExpression);ExpressionBuilder = new ExpressionBuilder(this);ServiceCtor = configurationExpression.ServiceCtor;EnableNullPropagationForQueryMapping = configurationExpression.EnableNullPropagationForQueryMapping ?? false;MaxExecutionPlanDepth = configurationExpression.Advanced.MaxExecutionPlanDepth + 1;ResultConverters = configurationExpression.Advanced.QueryableResultConverters.ToArray();Binders = configurationExpression.Advanced.QueryableBinders.ToArray();RecursiveQueriesMaxDepth = configurationExpression.Advanced.RecursiveQueriesMaxDepth;Configuration = new ProfileMap(configurationExpression);Profiles = new[] { Configuration }.Concat(configurationExpression.Profiles.Select(p => new ProfileMap(p, configurationExpression))).ToArray();configurationExpression.Features.Configure(this);foreach (var beforeSealAction in configurationExpression.Advanced.BeforeSealActions)beforeSealAction?.Invoke(this);Seal();}

在构造函数中实际就是将构建一个MapperConfigurationExpression表达式,然后将当前方法生成Action进行对象的映射,
表达式创建完成之后就进入到AutoMapper核心方法Seal方法
首先我们简单的看一下Seal方法

 private void Seal(){var derivedMaps = new List<Tuple<TypePair, TypeMap>>();var redirectedTypes = new List<Tuple<TypePair, TypePair>>();//获取所有的需要映射的集合 进行注册foreach (var profile in Profiles){//单个进行注册,传入当前对象profile.Register(this);}//IncludeAllDerivedTypes 子类型foreach (var typeMap in _configuredMaps.Values.Where(tm => tm.IncludeAllDerivedTypes)){//循环遍历获取可以赋值的派生类型foreach (var derivedMap in _configuredMaps.Where(tm =>typeMap.SourceType.IsAssignableFrom(tm.Key.SourceType) &&typeMap.DestinationType.IsAssignableFrom(tm.Key.DestinationType) &&typeMap != tm.Value).Select(tm => tm.Value)){//获取派生类型typeMap.IncludeDerivedTypes(derivedMap.SourceType, derivedMap.DestinationType);}}foreach (var profile in Profiles){profile.Configure(this);}foreach (var typeMap in _configuredMaps.Values){_resolvedMaps[typeMap.Types] = typeMap;if (typeMap.DestinationTypeOverride != null){redirectedTypes.Add(Tuple.Create(typeMap.Types, new TypePair(typeMap.SourceType, typeMap.DestinationTypeOverride)));}derivedMaps.AddRange(GetDerivedTypeMaps(typeMap).Select(derivedMap => Tuple.Create(new TypePair(derivedMap.SourceType, typeMap.DestinationType), derivedMap)));}foreach (var redirectedType in redirectedTypes){var derivedMap = FindTypeMapFor(redirectedType.Item2);if (derivedMap != null){_resolvedMaps[redirectedType.Item1] = derivedMap;}}foreach (var derivedMap in derivedMaps.Where(derivedMap => !_resolvedMaps.ContainsKey(derivedMap.Item1))){_resolvedMaps[derivedMap.Item1] = derivedMap.Item2;}foreach (var typeMap in _configuredMaps.Values){typeMap.Seal(this);}Features.Seal(this);}

在这里首先会获取source Type和destination Type的字段映射对象,然后将实现过IProfiles的方法获取到,并且进行注册(添加Mapper关系)

注册

 private void BuildTypeMap(IConfigurationProvider configurationProvider, ITypeMapConfiguration config){//创建类型映射对象//config.SourceType 需要转化的实体//config.DestinationType 被映射的实体// config.IsReverseMap 是否需要反向映射实体var typeMap = TypeMapFactory.CreateTypeMap(config.SourceType, config.DestinationType, this, config.IsReverseMap);config.Configure(typeMap);configurationProvider.RegisterTypeMap(typeMap);}

注册过程就是将需要被转化的实体和被映射的实体注册进TypeMap,最终添加MapperConfigurationExpression表达式中
注册完成之后就是获取到所有的派生类型进行注册

MapperConfigurationExpression表达式解析

当所有的类都已经做好关系映射之后,就进入了 profile.Configure(this)方法,这个方法就是解析MapperConfigurationExpression表达式进行映射。在此之后会进去一些配置映射操作

foreach (var typeMap in _configuredMaps.Values){typeMap.Seal(this);}public void Seal(IConfigurationProvider configurationProvider){if(_sealed){return;}_sealed = true;_inheritedTypeMaps.ForAll(tm => _includedMembersTypeMaps.UnionWith(tm._includedMembersTypeMaps));foreach (var includedMemberTypeMap in _includedMembersTypeMaps){includedMemberTypeMap.TypeMap.Seal(configurationProvider);ApplyIncludedMemberTypeMap(includedMemberTypeMap);}_inheritedTypeMaps.ForAll(tm => ApplyInheritedTypeMap(tm));_orderedPropertyMaps = PropertyMaps.OrderBy(map => map.MappingOrder).ToArray();_propertyMaps.Clear();MapExpression = CreateMapperLambda(configurationProvider, null);Features.Seal(configurationProvider);}

在typeMap.Seal会调用CreateDestinationFunc方法创建一个lambda表达式,内容是new 一个destination对象,在CreateAssignmentFunc方法中会对派生类进行赋值的lambda内容,其中规则就是在注册是使用的规则,但是在两个对象做映射的过程中会有字段没有对应上的属性,CreateMapperFunc会产生一些规则,比如默认值赋值等等。生成的规则会存储在MapExpresion表达式中。

总结

在使用AutoMapper的过程中,系统只会运行一次seal()方法,存储好对象之间的关系,最终调用的时候只会在已经存储好的对象中去寻找映射关系,最终达成映射(ps:当大家在使用过程中,如果不想某些字段进行映射,可以使用IgnoreMapAttribute标记,在配置规则的过程中,如有发现有标记IgnoreMapAttribute的字段,会自动忽略)

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

AutoMapper源码解析相关推荐

  1. 谷歌BERT预训练源码解析(二):模型构建

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...

  2. 谷歌BERT预训练源码解析(三):训练过程

    目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...

  3. 谷歌BERT预训练源码解析(一):训练数据生成

    目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...

  4. Gin源码解析和例子——中间件(middleware)

    在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...

  5. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  6. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  7. libev源码解析——定时器原理

    本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...

  8. libev源码解析——I/O模型

    在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...

  9. libev源码解析——调度策略

    在<libev源码解析--监视器(watcher)结构和组织形式>中介绍过,监视器分为[2,-2]区间5个等级的优先级.等级为2的监视器最高优,然后依次递减.不区分监视器类型和关联的文件描 ...

最新文章

  1. qt4的quick程序升级到qt5_最新8月书单出炉!送给你程序员
  2. mogilefsd同步速度调优
  3. C#LeetCode刷题之#645-错误的集合(Set Mismatch)
  4. rediscli shell_redis shell
  5. 京东面试官:SQL 语句中 left join 后用 on 还是 where,区别大了!
  6. 自定mvc之新增,下架以及上架
  7. mac刷新dns、windows刷新dns、刷新dns缓存命令
  8. 零基础C语言入门007——ascⅡ字符表、字符常量
  9. 高校学生竞赛信息管理系统介绍
  10. [4G5G专题-81]:流程 - 4G LTE 小区切换流程大全
  11. 通达信 c 语言,通达信C
  12. 判断矩形相似C语言,C++判断矩形相交的方法
  13. 王亮:游戏AI探索之旅——从alphago到moba游戏
  14. 看看阿里双十一970P数据处理得,那叫一个牛啤!
  15. 调出cmd输入时的光标
  16. 【愿头发与你我同在队】团队项目第一次作业-组队与选题
  17. [uboot]What is MLO file?
  18. 【Python网络蜘蛛 · 6】:明文和密文的转换、json模块的使用、jsonpath模块的使用(附源代码)
  19. zk - zookeeper主节点、从节点、客户端三者之间的交互
  20. python:split()函数

热门文章

  1. busybox tools
  2. 2.语法特性(组合数据类型)
  3. 2022最新网络安全零基础学习路线
  4. linux永久自动挂载
  5. HSV颜色分割,RGB与HSV颜色空间的关系
  6. IT新人到底该不该去外包公司?
  7. IC数字芯片学习各类公众号汇总
  8. 字符串函数剖析(3)---strstr函数
  9. 把已有普通文件夹变成git仓库以及git常用命令的使用
  10. Python之print打印