最近有一個小項目須要提供接口給第三方使用,接口會獲得一個大的XML的字符串大約有8個對象100多個字段,在映射到Entity只能經過反射來賦值避免重複的賦值,可是明顯感受到性能降低嚴重,由於之前接觸過AutoMapper因此寫了一篇博客記錄其中的實現原理。git
在github 上能夠下載AutoMapper 源碼,直接打開sln 文件 就能夠看到項目結構。github
項目結構很是清晰,一個AutoMapper的實現類,兩個測試庫一個單元測試一個集成測試還有一個性能測試。下面就是AutoMapper的測試代碼。安全
var config = new MapperConfiguration(cfg => cfg.CreateMap<TestSource, TestDestination>() ); var mapper1 = config.CreateMapper(); var fooDto = mapper1.Map<TestDestination>(new TestSource{ MyProperty = 1 });
項目不大,調試的時候你就能夠明白AutoMapper的實現原理,下面就是一個大體的分析過程。app
代碼建立了一個MapperConfiguration對象,裏面傳遞了一個Action到構造方法,而這個Action裏建立了Map 的信息,這是最簡單的demo因此使用默認的字段Map方式。ide
1 public MapperConfiguration(Action<IMapperConfigurationExpression> configure) 2 : this(Build(configure)) 3 { 4 } 5 6 private static MapperConfigurationExpression Build(Action<IMapperConfigurationExpression> configure) 7 { 8 var expr = new MapperConfigurationExpression(); 9 configure(expr); 10 return expr; 11 }
上面就是構造方法處理的邏輯,實際上就是build方法創造一個MapperConfigurationExpression對象,而後把這個對象傳給Action生成Mapper,這個Mapper的意思就是source Type和destination Type的字段映射對象。咱們繼續查看這個第8行的代碼在new這個對象的時候作了什麼初始化操做。這個MapperConfigurationExpression繼承了抽象類Profile,而在這個父類的構造方法裏初始化了IMemberConfiguration類,這個是爲了制定map規則對象,默認是DefaultMember這個對象。下面是這個建立完MapperConfigurationExpression後調用構造方法。性能
1 public MapperConfiguration(MapperConfigurationExpression configurationExpression) 2 { 3 _mappers = configurationExpression.Mappers.ToArray(); 4 _resolvedMaps = new LockingConcurrentDictionary<TypePair, TypeMap>(GetTypeMap); 5 _executionPlans = new LockingConcurrentDictionary<MapRequest, Delegate>(CompileExecutionPlan); 6 _validator = new ConfigurationValidator(this, configurationExpression); 7 ExpressionBuilder = new ExpressionBuilder(this); 8 9 ServiceCtor = configurationExpression.ServiceCtor; 10 EnableNullPropagationForQueryMapping = configurationExpression.EnableNullPropagationForQueryMapping ?? false; 11 MaxExecutionPlanDepth = configurationExpression.Advanced.MaxExecutionPlanDepth + 1; 12 ResultConverters = configurationExpression.Advanced.QueryableResultConverters.ToArray(); 13 Binders = configurationExpression.Advanced.QueryableBinders.ToArray(); 14 RecursiveQueriesMaxDepth = configurationExpression.Advanced.RecursiveQueriesMaxDepth; 15 16 Configuration = new ProfileMap(configurationExpression); 17 Profiles = new[] { Configuration }.Concat(configurationExpression.Profiles.Select(p => new ProfileMap(p, configurationExpression))).ToArray(); 18 19 configurationExpression.Features.Configure(this); 20 21 foreach (var beforeSealAction in configurationExpression.Advanced.BeforeSealActions) 22 beforeSealAction?.Invoke(this); 23 Seal(); 24 }
在這個構造方法裏會將以前建立的MapperConfigurationExpression轉化成當前對象的各個字段,其中LockingConcurrentDictionary是本身實現的線程安全的字典對象,構造方法傳遞取值的規則,重要的是Profiles字段,這個存儲以前的action 傳遞的MapperConfigurationExpression對象,這個對象也就是source type和destination type 對象的相關信息。最後重要的是seal 方法,根據相關信息生產lambda代碼。單元測試
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); }
上面就是核心代碼,根據以前註冊的相關信息生成一個lambda代碼。CreateDestinationFunc建立一個lambda表達式,內容是new 一個destination對象,在CreateAssignmentFunc繼續擴展字段賦值的lambda內容,其中字段map規則就是以前新建MapperConfiguration測試
對象的時候建立的,若是沒有注入就是默認的匹配規則,CreateMapperFunc會產生一些規則,好比默認值賦值等等。這些生成的lambda對象會存在Typemap 對象的MapExpression中。ui
1 public LambdaExpression CreateMapperLambda(HashSet<TypeMap> typeMapsPath) 2 { 3 var customExpression = TypeConverterMapper() ?? _typeMap.CustomMapFunction ?? _typeMap.CustomMapExpression; 4 if(customExpression != null) 5 { 6 return Lambda(customExpression.ReplaceParameters(Source, _initialDestination, Context), Source, _initialDestination, Context); 7 } 8 9 CheckForCycles(typeMapsPath); 10 11 if(typeMapsPath != null) 12 { 13 return null; 14 } 15 16 var destinationFunc = CreateDestinationFunc(); 17 18 var assignmentFunc = CreateAssignmentFunc(destinationFunc); 19 20 var mapperFunc = CreateMapperFunc(assignmentFunc); 21 22 var checkContext = CheckContext(_typeMap, Context); 23 var lambaBody = checkContext != null ? new[] {checkContext, mapperFunc} : new[] {mapperFunc}; 24 25 return Lambda(Block(new[] {_destination}, lambaBody), Source, _initialDestination, Context); 26 }
後來的調用map的時候就會這些lambda方法,在以前說過,生成的lambda表達式會在內存中動態生成IL代碼,這些花費的時間只有一次就是seal調用的時候,後面的時候去運行代碼速度會快得多,由於這些動態生成的il代碼在運行的時候速度和手寫代碼運行的同樣的,因此代碼運行的時候是很是快的,遠遠高於反射的速度,因此這就是AutoMapper運行速度快的緣由。這個項目挺小的而且代碼工整可讀性挺強的,但願你們在閱讀源代碼可以學的更多。最後謝謝你們。this