Skip to content

Commit e3aecd3

Browse files
committed
reuse the execution plan between user Map calls and internal Map calls
1 parent 77086e8 commit e3aecd3

File tree

7 files changed

+17
-52
lines changed

7 files changed

+17
-52
lines changed

src/AutoMapper/Configuration/MapperConfiguration.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,15 @@ public MapperConfiguration(Action<IMapperConfigurationExpression> configure)
173173
public IMapper CreateMapper(Func<Type, object> serviceCtor) => new Mapper(this, serviceCtor);
174174
public void CompileMappings()
175175
{
176-
foreach (var request in _resolvedMaps.Keys.Where(t => !t.IsGenericTypeDefinition).Select(types => new MapRequest(types, types)).ToArray())
176+
foreach (var request in _resolvedMaps.Keys.Where(t => !t.IsGenericTypeDefinition).Select(types => new MapRequest(types, types, MemberMap.Instance)).ToArray())
177177
{
178178
GetExecutionPlan(request);
179179
}
180180
}
181181
public LambdaExpression BuildExecutionPlan(Type sourceType, Type destinationType)
182182
{
183183
var typePair = new TypePair(sourceType, destinationType);
184-
return this.Internal().BuildExecutionPlan(new(typePair, typePair));
184+
return this.Internal().BuildExecutionPlan(new(typePair, typePair, MemberMap.Instance));
185185
}
186186
LambdaExpression IGlobalConfiguration.BuildExecutionPlan(in MapRequest mapRequest)
187187
{
@@ -237,7 +237,7 @@ LambdaExpression GenerateObjectMapperExpression(in MapRequest mapRequest, IObjec
237237
var throwExpression = Throw(newException, runtimeDestinationType);
238238
fullExpression = TryCatch(ToType(map, runtimeDestinationType), Catch(ExceptionParameter, throwExpression));
239239
}
240-
var profileMap = mapRequest.MemberMap?.Profile ?? Configuration;
240+
var profileMap = mapRequest.MemberMap.Profile ?? Configuration;
241241
var nullCheckSource = NullCheckSource(profileMap, source, destination, fullExpression, mapRequest.MemberMap);
242242
return Lambda(nullCheckSource, source, destination, ContextParameter);
243243
}

src/AutoMapper/Execution/ExpressionBuilder.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static class ExpressionBuilder
3131
public static readonly MethodInfo OverTypeDepthMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.OverTypeDepth));
3232
public static readonly MethodInfo CacheDestinationMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.CacheDestination));
3333
public static readonly MethodInfo GetDestinationMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.GetDestination));
34-
private static readonly MethodCallExpression CheckContextCall = Expression.Call(
34+
public static readonly MethodCallExpression CheckContextCall = Expression.Call(
3535
typeof(ResolutionContext).GetStaticMethod(nameof(ResolutionContext.CheckContext)), ContextParameter);
3636
private static readonly MethodInfo ContextMapMethod = typeof(ResolutionContext).GetInstanceMethod(nameof(ResolutionContext.MapInternal));
3737
private static readonly MethodInfo ArrayEmptyMethod = typeof(Array).GetStaticMethod(nameof(Array.Empty));
@@ -81,9 +81,7 @@ public static Expression NullCheckSource(ProfileMap profileMap, Expression sourc
8181
var destinationType = destinationParameter.Type;
8282
var isCollection = destinationType.IsCollection();
8383
var mustUseDestination = memberMap is { MustUseDestination: true };
84-
var ifSourceNull = memberMap == null ?
85-
destinationParameter.IfNullElse(DefaultDestination(), ClearDestinationCollection()) :
86-
mustUseDestination ? ClearDestinationCollection() : DefaultDestination();
84+
var ifSourceNull = mustUseDestination ? ClearDestinationCollection() : DefaultDestination();
8785
return sourceParameter.IfNullElse(ifSourceNull, mapExpression);
8886
Expression ClearDestinationCollection()
8987
{
@@ -109,13 +107,13 @@ Expression ClearDestinationCollection()
109107
}
110108
var destinationElementType = GetEnumerableElementType(destinationType);
111109
destinationCollectionType = typeof(ICollection<>).MakeGenericType(destinationElementType);
112-
collection = Convert(collection, destinationCollectionType);
110+
collection = TypeAs(collection, destinationCollectionType);
113111
}
114112
clearMethod = destinationCollectionType.GetMethod("Clear");
115113
}
116114
return Block(new[] { destinationVariable },
117115
Assign(destinationVariable, destinationParameter),
118-
Condition(ReferenceEqual(destinationVariable, Null), Empty, Expression.Call(collection, clearMethod)),
116+
Condition(ReferenceEqual(collection, Null), Empty, Expression.Call(collection, clearMethod)),
119117
destinationVariable);
120118
}
121119
Expression DefaultDestination()
@@ -141,14 +139,6 @@ public static Expression ContextMap(TypePair typePair, Expression sourceParamete
141139
var mapMethod = ContextMapMethod.MakeGenericMethod(typePair.SourceType, typePair.DestinationType);
142140
return Expression.Call(ContextParameter, mapMethod, sourceParameter, destinationParameter, Constant(memberMap, typeof(MemberMap)));
143141
}
144-
public static Expression CheckContext(TypeMap typeMap)
145-
{
146-
if (typeMap.MaxDepth > 0 || typeMap.PreserveReferences)
147-
{
148-
return CheckContextCall;
149-
}
150-
return null;
151-
}
152142
public static Expression OverMaxDepth(TypeMap typeMap) => typeMap?.MaxDepth > 0 ? Expression.Call(ContextParameter, OverTypeDepthMethod, Constant(typeMap)) : null;
153143
public static Expression NullSubstitute(this MemberMap memberMap, Expression sourceExpression) =>
154144
Coalesce(sourceExpression, ToType(Constant(memberMap.NullSubstitute), sourceExpression.Type));

src/AutoMapper/Execution/TypeMapPlanBuilder.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,9 @@ public LambdaExpression CreateMapperLambda(HashSet<TypeMap> typeMapsPath)
5454
var createDestinationFunc = CreateDestinationFunc();
5555
var assignmentFunc = CreateAssignmentFunc(createDestinationFunc);
5656
var mapperFunc = CreateMapperFunc(assignmentFunc);
57-
var checkContext = CheckContext(_typeMap);
58-
if (checkContext != null)
57+
if (_typeMap.MaxDepth > 0 || _typeMap.PreserveReferences)
5958
{
60-
statements.Add(checkContext);
59+
statements.Add(CheckContextCall);
6160
}
6261
statements.Add(mapperFunc);
6362
variables.Add(_destination);

src/AutoMapper/Internal/TypePair.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ namespace AutoMapper.Internal
88
public readonly TypePair RequestedTypes;
99
public readonly TypePair RuntimeTypes;
1010
public readonly MemberMap MemberMap;
11-
public MapRequest(TypePair requestedTypes, TypePair runtimeTypes, MemberMap memberMap = null)
11+
public MapRequest(TypePair requestedTypes, TypePair runtimeTypes, MemberMap memberMap)
1212
{
1313
RequestedTypes = requestedTypes;
1414
RuntimeTypes = runtimeTypes;
1515
MemberMap = memberMap;
1616
}
1717
public bool Equals(MapRequest other) => RequestedTypes.Equals(other.RequestedTypes) && RuntimeTypes.Equals(other.RuntimeTypes) &&
18-
(MemberMap == other.MemberMap || (MemberMap?.MapperEquals(other.MemberMap)).GetValueOrDefault());
18+
(MemberMap == other.MemberMap || MemberMap.MapperEquals(other.MemberMap));
1919
public override bool Equals(object obj) => obj is MapRequest other && Equals(other);
20-
public override int GetHashCode() => HashCode.Combine(RequestedTypes, RuntimeTypes, MemberMap?.MapperGetHashCode());
20+
public override int GetHashCode() => HashCode.Combine(RequestedTypes, RuntimeTypes, MemberMap.MapperGetHashCode());
2121
public static bool operator ==(in MapRequest left, in MapRequest right) => left.Equals(right);
2222
public static bool operator !=(in MapRequest left, in MapRequest right) => !left.Equals(right);
2323
}

src/AutoMapper/Mapper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ private TDestination MapCore<TSource, TDestination>(
198198
{
199199
TypePair requestedTypes = new(typeof(TSource), typeof(TDestination));
200200
TypePair runtimeTypes = new(source?.GetType() ?? sourceType ?? typeof(TSource), destination?.GetType() ?? destinationType ?? typeof(TDestination));
201+
memberMap ??= destination == null ? MemberMap.Instance : MemberMap.InstanceUseDestination;
201202
MapRequest mapRequest = new(requestedTypes, runtimeTypes, memberMap);
202203
return _configurationProvider.GetExecutionPlan<TSource, TDestination>(mapRequest)(source, destination, context);
203204
}

src/AutoMapper/Mappers/CollectionMapper.cs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,12 @@ Expression MapCollectionCore(Expression destExpression)
7474
addItems = Condition(overMaxDepth, ExpressionBuilder.Empty, addItems);
7575
}
7676
var clearMethod = isIList ? IListClear : destinationCollectionType.GetMethod("Clear");
77-
var checkNull = Block(new[] { newExpression, passedDestination },
77+
return Block(new[] { newExpression, passedDestination },
7878
Assign(passedDestination, destExpression),
7979
assignNewExpression,
8080
Call(destination, clearMethod),
8181
addItems,
8282
destination);
83-
if (memberMap != null)
84-
{
85-
return checkNull;
86-
}
87-
return CheckContext();
8883
void GetDestinationType()
8984
{
9085
var immutableCollection = !mustUseDestination && destinationType.IsValueType;
@@ -133,20 +128,6 @@ void UseDestinationValue()
133128
assignNewExpression = Assign(newExpression, Coalesce(passedDestination, ObjectFactory.GenerateConstructorExpression(passedDestination.Type)));
134129
}
135130
}
136-
Expression CheckContext()
137-
{
138-
var elementTypeMap = configurationProvider.ResolveTypeMap(sourceElementType, destinationElementType);
139-
if (elementTypeMap == null)
140-
{
141-
return checkNull;
142-
}
143-
var checkContext = ExpressionBuilder.CheckContext(elementTypeMap);
144-
if (checkContext == null)
145-
{
146-
return checkNull;
147-
}
148-
return Block(checkContext, checkNull);
149-
}
150131
}
151132
}
152133
private static Expression CreateNameValueCollection(Expression sourceExpression) =>

src/AutoMapper/MemberMap.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public class MemberMap : IValueResolver
1818
{
1919
private static readonly LambdaExpression EmptyLambda = Lambda(ExpressionBuilder.Null);
2020
protected MemberMap(TypeMap typeMap = null) => TypeMap = typeMap;
21-
public static readonly MemberMap Instance = new();
21+
internal static readonly MemberMap Instance = new();
22+
internal static readonly MemberMap InstanceUseDestination = new PropertyMap(default(MemberInfo), null, null) { UseDestinationValue = true };
2223
public TypeMap TypeMap { get; protected set; }
2324
public LambdaExpression CustomMapExpression => Resolver?.ProjectToExpression;
2425
public bool IsResolveConfigured => Resolver != null && Resolver != this;
@@ -64,15 +65,8 @@ public Expression ChainSourceMembers(Expression source, Type destinationType, Ex
6465
public bool AllowsNullCollections => (Profile?.AllowsNullCollectionsFor(this)).GetValueOrDefault();
6566
public ProfileMap Profile => TypeMap?.Profile;
6667
private int MaxDepth => (TypeMap?.MaxDepth).GetValueOrDefault();
67-
public bool MapperEquals(MemberMap other)
68-
{
69-
if (other == null)
70-
{
71-
return false;
72-
}
73-
return other.MustUseDestination == MustUseDestination && other.MaxDepth == MaxDepth &&
68+
public bool MapperEquals(MemberMap other) => other.MustUseDestination == MustUseDestination && other.MaxDepth == MaxDepth &&
7469
other.AllowsNullDestinationValues == AllowsNullDestinationValues && other.AllowsNullCollections == AllowsNullCollections;
75-
}
7670
public int MapperGetHashCode() => HashCode.Combine(MustUseDestination, MaxDepth, AllowsNullDestinationValues, AllowsNullCollections);
7771
protected Type GetSourceType() => Resolver?.ResolvedType ?? DestinationType;
7872
public void MapByConvention(MemberInfo[] sourceMembers)

0 commit comments

Comments
 (0)