tools/AutoMapper/Execution/ExpressionBuilder.cs (114 lines of code) (raw):

namespace AutoMapper.Execution { using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using AutoMapper.Configuration; using AutoMapper.Internal; using AutoMapper.Mappers.Internal; using static System.Linq.Expressions.Expression; public static class ExpressionBuilder { private static readonly Expression<Func<IRuntimeMapper, ResolutionContext>> CreateContext = mapper => new ResolutionContext(mapper.DefaultContext.Options, mapper); private static readonly MethodInfo ContextMapMethod = ExpressionFactory.Method<ResolutionContext, object>(a => a.Map<object, object>(null, null)).GetGenericMethodDefinition(); public static Expression MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, Expression contextParameter, PropertyMap propertyMap = null, Expression destinationParameter = null) { if (destinationParameter == null) destinationParameter = Default(typePair.DestinationType); var typeMap = configurationProvider.ResolveTypeMap(typePair); if (typeMap != null) { if (!typeMap.HasDerivedTypesToInclude()) { typeMap.Seal(configurationProvider); return typeMap.MapExpression != null ? typeMap.MapExpression.ConvertReplaceParameters(sourceParameter, destinationParameter, contextParameter) : ContextMap(typePair, sourceParameter, contextParameter, destinationParameter); } return ContextMap(typePair, sourceParameter, contextParameter, destinationParameter); } var objectMapperExpression = ObjectMapperExpression(configurationProvider, profileMap, typePair, sourceParameter, contextParameter, propertyMap, destinationParameter); var nullCheckSource = NullCheckSource(profileMap, sourceParameter, destinationParameter, objectMapperExpression, propertyMap); return ExpressionFactory.ToType(nullCheckSource, typePair.DestinationType); } public static Expression NullCheckSource(ProfileMap profileMap, Expression sourceParameter, Expression destinationParameter, Expression objectMapperExpression, PropertyMap propertyMap = null) { var declaredDestinationType = destinationParameter.Type; var destinationType = objectMapperExpression.Type; var defaultDestination = DefaultDestination(destinationType, declaredDestinationType, profileMap); var destination = propertyMap == null ? destinationParameter.IfNullElse(defaultDestination, destinationParameter) : (propertyMap.UseDestinationValue ? destinationParameter : defaultDestination); var ifSourceNull = destinationParameter.Type.IsCollectionType() ? ClearDestinationCollection() : destination; return sourceParameter.IfNullElse(ifSourceNull, objectMapperExpression); Expression ClearDestinationCollection() { var destinationElementType = ElementTypeHelper.GetElementType(destinationParameter.Type); var destinationCollectionType = typeof(ICollection<>).MakeGenericType(destinationElementType); var destinationVariable = Variable(destinationCollectionType, "collectionDestination"); var clear = Call(destinationVariable, destinationCollectionType.GetDeclaredMethod("Clear")); var isReadOnly = Property(destinationVariable, "IsReadOnly"); return Block(new[] {destinationVariable}, Assign(destinationVariable, ExpressionFactory.ToType(destinationParameter, destinationCollectionType)), Condition(OrElse(Equal(destinationVariable, Constant(null)), isReadOnly), Empty(), clear), destination); } } private static Expression DefaultDestination(Type destinationType, Type declaredDestinationType, ProfileMap profileMap) { if(profileMap.AllowNullCollections || destinationType == typeof(string) || !destinationType.IsEnumerableType()) { return Default(declaredDestinationType); } if(destinationType.IsArray) { var destinationElementType = destinationType.GetElementType(); return NewArrayBounds(destinationElementType, Enumerable.Repeat(Constant(0), destinationType.GetArrayRank())); } return DelegateFactory.GenerateNonNullConstructorExpression(destinationType); } private static Expression ObjectMapperExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, Expression contextParameter, PropertyMap propertyMap, Expression destinationParameter) { var match = configurationProvider.FindMapper(typePair); if (match != null) { var mapperExpression = match.MapExpression(configurationProvider, profileMap, propertyMap, sourceParameter, destinationParameter, contextParameter); return mapperExpression; } return ContextMap(typePair, sourceParameter, contextParameter, destinationParameter); } public static Expression ContextMap(TypePair typePair, Expression sourceParameter, Expression contextParameter, Expression destinationParameter) { var mapMethod = ContextMapMethod.MakeGenericMethod(typePair.SourceType, typePair.DestinationType); return Call(contextParameter, mapMethod, sourceParameter, destinationParameter); } public static ConditionalExpression CheckContext(TypeMap typeMap, Expression context) { if (typeMap.MaxDepth > 0 || typeMap.PreserveReferences) { var mapper = Property(context, "Mapper"); return IfThen(Property(context, "IsDefault"), Assign(context, Invoke(CreateContext, mapper))); } return null; } } }