tools/AutoMapper/Mappers/Internal/CollectionMapperExpressionFactory.cs (104 lines of code) (raw):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using AutoMapper.Execution;
using AutoMapper.Internal;
namespace AutoMapper.Mappers.Internal
{
using static Expression;
using static ExpressionBuilder;
using static ExpressionFactory;
using static ElementTypeHelper;
public static class CollectionMapperExpressionFactory
{
public delegate Expression MapItem(IConfigurationProvider configurationProvider, ProfileMap profileMap,
Type sourceType, Type destType, Expression contextParam,
out ParameterExpression itemParam);
public static Expression MapCollectionExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression, Type ifInterfaceType, MapItem mapItem)
{
var passedDestination = Variable(destExpression.Type, "passedDestination");
var newExpression = Variable(passedDestination.Type, "collectionDestination");
var sourceElementType = GetElementType(sourceExpression.Type);
var itemExpr = mapItem(configurationProvider, profileMap, sourceExpression.Type, passedDestination.Type,
contextExpression, out ParameterExpression itemParam);
var destinationElementType = itemExpr.Type;
var destinationCollectionType = typeof(ICollection<>).MakeGenericType(destinationElementType);
if (!destinationCollectionType.IsAssignableFrom(destExpression.Type))
destinationCollectionType = typeof(IList);
var addMethod = destinationCollectionType.GetDeclaredMethod("Add");
Expression destination, assignNewExpression;
UseDestinationValue();
var addItems = ForEach(sourceExpression, itemParam, Call(destination, addMethod, itemExpr));
var mapExpr = Block(addItems, destination);
var clearMethod = destinationCollectionType.GetDeclaredMethod("Clear");
var checkNull =
Block(new[] { newExpression, passedDestination },
Assign(passedDestination, destExpression),
assignNewExpression,
Call(destination, clearMethod),
mapExpr
);
if (propertyMap != null)
return checkNull;
var elementTypeMap = configurationProvider.ResolveTypeMap(sourceElementType, destinationElementType);
if (elementTypeMap == null)
return checkNull;
var checkContext = CheckContext(elementTypeMap, contextExpression);
if (checkContext == null)
return checkNull;
return Block(checkContext, checkNull);
void UseDestinationValue()
{
if(propertyMap?.UseDestinationValue == true)
{
destination = passedDestination;
assignNewExpression = Empty();
}
else
{
destination = newExpression;
Expression createInstance = passedDestination.Type.NewExpr(ifInterfaceType);
var isReadOnly = Property(ToType(passedDestination, destinationCollectionType), "IsReadOnly");
assignNewExpression = Assign(newExpression,
Condition(OrElse(Equal(passedDestination, Constant(null)), isReadOnly), ToType(createInstance, passedDestination.Type), passedDestination));
}
}
}
private static Expression NewExpr(this Type baseType, Type ifInterfaceType)
{
var newExpr = baseType.IsInterface()
? New(
ifInterfaceType.MakeGenericType(GetElementTypes(baseType,
ElementTypeFlags.BreakKeyValuePair)))
: DelegateFactory.GenerateConstructorExpression(baseType);
return newExpr;
}
public static Expression MapItemExpr(IConfigurationProvider configurationProvider, ProfileMap profileMap, Type sourceType, Type destType, Expression contextParam, out ParameterExpression itemParam)
{
var sourceElementType = GetElementType(sourceType);
var destElementType = GetElementType(destType);
itemParam = Parameter(sourceElementType, "item");
var typePair = new TypePair(sourceElementType, destElementType);
var itemExpr = MapExpression(configurationProvider, profileMap, typePair, itemParam, contextParam);
return ToType(itemExpr, destElementType);
}
public static Expression MapKeyPairValueExpr(IConfigurationProvider configurationProvider, ProfileMap profileMap, Type sourceType, Type destType, Expression contextParam, out ParameterExpression itemParam)
{
var sourceElementTypes = GetElementTypes(sourceType, ElementTypeFlags.BreakKeyValuePair);
var destElementTypes = GetElementTypes(destType, ElementTypeFlags.BreakKeyValuePair);
var typePairKey = new TypePair(sourceElementTypes[0], destElementTypes[0]);
var typePairValue = new TypePair(sourceElementTypes[1], destElementTypes[1]);
var sourceElementType = typeof(KeyValuePair<,>).MakeGenericType(sourceElementTypes);
itemParam = Parameter(sourceElementType, "item");
var destElementType = typeof(KeyValuePair<,>).MakeGenericType(destElementTypes);
var keyExpr = MapExpression(configurationProvider, profileMap, typePairKey,
Property(itemParam, "Key"), contextParam);
var valueExpr = MapExpression(configurationProvider, profileMap, typePairValue,
Property(itemParam, "Value"), contextParam);
var keyPair = New(destElementType.GetDeclaredConstructors().First(), keyExpr, valueExpr);
return keyPair;
}
}
}