研究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的欄位,會自動忽略)
如有哪裡講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧