Before studying AutoMapper source, let’s take a look at the function of AutoMapper


AutoMapper is a simple little library designed to solve seemingly complex problems – getting rid of the code interpretation that maps one object to another

First, a simple demonstration using the AutoMapper method

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

The constructor

In this code, the MapperConfiguration object is created by default, and an Action with a mapping is passed in. When the MapperConfiguration is created, the constructor is executed by default

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(); }Copy the code

In the constructor is to actually build a MapperConfigurationExpression expression, then the current object mapping method to generate the Action, The Seal method is the core method of the AutoMapper. First let’s take a quick look at the Seal method

private void Seal() { var derivedMaps = new List<Tuple<TypePair, TypeMap>>(); var redirectedTypes = new List<Tuple<TypePair, TypePair>>(); Foreach (var profile in Profiles) {// To Register a single object, pass the current object profile.register (this); } / / IncludeAllDerivedTypes subtype foreach (var it typeMap in _configuredMaps. Values. The 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. The Value). The Select (tm = > tm. Value)) {/ / get the derived type. It 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); }Copy the code

In this case, the source Type and Destination Type field mapping objects are first obtained, then the IProfiles implemented method is obtained and registered (adding Mapper relationship).

registered

private void BuildTypeMap(IConfigurationProvider configurationProvider, ITypeMapConfiguration config) {// Create a type mapping object //config.SourceType Entity to be converted //config.DestinationType Entity to be mapped // Config. Whether you need IsReverseMap reverse mapping entity var. It typeMap = TypeMapFactory CreateTypeMap (config. SourceType, config DestinationType, this, config.IsReverseMap); config.Configure(typeMap); configurationProvider.RegisterTypeMap(typeMap); }Copy the code

Registration process is the entity will need to be transformed and mapped entity registered into it TypeMap, eventually add MapperConfigurationExpression expression of registration is completed is access to all the derived type to register

MapperConfigurationExpression expression parsing

When all the class has done well after the relational mapping, into the profile. The Configure (this) method, this method is analytical mapping MapperConfigurationExpression expression. After that there will be some configuration mapping operations

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);
        }

Copy the code

In typemap. Seal, the CreateDestinationFunc method is called to create a lambda expression that new a Destination object. In CreateAssignmentFunc, the content of the lambda is assigned to the derived class. Rules are used in registration, but in the process of mapping two objects, there will be fields without corresponding attributes. CreateMapperFunc will generate some rules, such as default value assignment and so on. The generated rules are stored in MapExpresion expressions.

conclusion

In the process of using AutoMapper, the system only runs the seal() method once to store the relationship between objects. When it is called, it only looks for the mapping between the stored objects and finally achieves the mapping (ps: If you do not want certain fields to be mapped, use the IgnoreMapAttribute tag. The IgnoreMapAttribute tag is automatically ignored during the rule configuration.

Welcome to correct me if I am not very clear or make mistakes

If you like, you might as well click a "like" to collect itCopy the code