研究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. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. C语言测一个浮点数的位数长度

    测浮点数的位数牵扯到一个精度的问题,用普通的测整形数值的方法不能实现,于是我自己写了一个测浮点数的函数. #include <stdio.h> //for printf int lengt ...

  2. 智能BPOS小票模板字体的使用

    关于伯俊智能BPOS零售小票模板的设置,常用字体"黑体"."宋体"."Arial"这几个字体,在设置的时候看似没有问题, 但是在正真使用打印 ...

  3. OneBlog开源博客-详细介绍如何实现freemarker自定义标签

    前言 OneBlog中使用到了springboot + freemarker的技术,同时项目里多个controller中都需要查询一个公有的数据集合,一般做法是直接在每个controller的方法中通 ...

  4. mapboxgl实现带箭头轨迹线

    最近在使用mapboxgl实现轨迹展示时,想实现类似高德地图导航轨迹效果,然而并未在网上找到类似示例.经一番研究与尝试,最终解决,效果如下. 添加箭头核心代码如下,只需在配置layout中添加symb ...

  5. livy提交spark应用

      spark-submit的使用shell时时灵活性较低,livy作为spark提交的一种工具,是使用接口或者java客户端的方式提交,可以集成到web应用中 1.客户端提交的方式 http://l ...

  6. WPF Line 的颜色过度动画

    <Line Grid.Column="2" Grid.ColumnSpan="2" VerticalAlignment="Center" ...

  7. 关于使用th:text获取不到值

    今天在使用thymeleaf模板引擎整合SpringBoot时,对于从controller层传递过来的参数"message",无法获取. 控制层代码如下: @PostMapping ...

  8. maven仓库和镜像

    目录 简介 本地仓库 远程仓库 远程仓库的更新 远程仓库的认证 部署到远程仓库 快照版本 依赖解析 镜像 本文主要是针对<maven实战>书中关键知识点的学习记录,未免有纰漏或描述不到之处 ...

  9. 关于使用jq跨域请求的实现

    今天算是把js跨域请求搞定了,想实现跨越,首先想到的是JSONP,但是具体去做的时候,发现有很多坑.在本地测试好之后又发现目标网站做了https证书认证,也就是实用的jsonp请求地址必须是https ...

  10. Linux 文件查看相关的一些命令

    文件压缩解压命令 # 解压 xxx.xz 并删除 xz -d test.tar.xz # 打包成 xxx.tar , 语法: tar -cvf 最后包名.tar ./要打包文件 ./要打包的文件 ta ...