-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Moving inline map creation back to inside type map resolving #2403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
72f0144
9b2bf58
48d9dc0
ae99680
74147f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ namespace AutoMapper | |
|
|
||
| public class MapperConfiguration : IConfigurationProvider | ||
| { | ||
| private static readonly Type[] ExcludedTypes = { typeof(object), typeof(ValueType), typeof(Enum) }; | ||
| private static readonly ConstructorInfo ExceptionConstructor = typeof(AutoMapperMappingException).GetDeclaredConstructors().Single(c => c.GetParameters().Length == 3); | ||
|
|
||
| private readonly IEnumerable<IObjectMapper> _mappers; | ||
|
|
@@ -110,19 +111,12 @@ public LambdaExpression BuildExecutionPlan(Type sourceType, Type destinationType | |
|
|
||
| public LambdaExpression BuildExecutionPlan(MapRequest mapRequest) | ||
| { | ||
| var typeMap = ResolveTypeMap(mapRequest.RuntimeTypes) ?? ResolveTypeMap(mapRequest.RequestedTypes); | ||
| var typeMap = ResolveTypeMap(mapRequest.RuntimeTypes, mapRequest.InlineConfig) ?? ResolveTypeMap(mapRequest.RequestedTypes, mapRequest.InlineConfig); | ||
| if (typeMap != null) | ||
| { | ||
| return GenerateTypeMapExpression(mapRequest, typeMap); | ||
| } | ||
| var mapperToUse = FindMapper(mapRequest.RuntimeTypes); | ||
| if (Configuration.CreateMissingTypeMaps && mapperToUse == null) | ||
| { | ||
| typeMap = Configuration.CreateInlineMap(_typeMapRegistry, mapRequest.InlineConfig ?? new DefaultTypeMapConfig(mapRequest.RuntimeTypes)); | ||
| _typeMapPlanCache[mapRequest.RuntimeTypes] = typeMap; | ||
| typeMap.Seal(this); | ||
| return GenerateTypeMapExpression(mapRequest, typeMap); | ||
| } | ||
| return GenerateObjectMapperExpression(mapRequest, mapperToUse, this); | ||
| } | ||
|
|
||
|
|
@@ -193,17 +187,28 @@ public TypeMap ResolveTypeMap(Type sourceType, Type destinationType) | |
| { | ||
| var typePair = new TypePair(sourceType, destinationType); | ||
|
|
||
| return ResolveTypeMap(typePair); | ||
| return ResolveTypeMap(typePair, new DefaultTypeMapConfig(typePair)); | ||
| } | ||
|
|
||
| public TypeMap ResolveTypeMap(Type sourceType, Type destinationType, ITypeMapConfiguration inlineConfiguration) | ||
| { | ||
| var typePair = new TypePair(sourceType, destinationType); | ||
|
|
||
| return ResolveTypeMap(typePair, inlineConfiguration); | ||
| } | ||
|
|
||
| public TypeMap ResolveTypeMap(TypePair typePair) | ||
| => ResolveTypeMap(typePair, new DefaultTypeMapConfig(typePair)); | ||
|
|
||
| public TypeMap ResolveTypeMap(TypePair typePair, ITypeMapConfiguration inlineConfiguration) | ||
| { | ||
| var typeMap = _typeMapPlanCache.GetOrAdd(typePair); | ||
| // if it's a dynamically created type map, we need to seal it outside GetTypeMap to handle recursion | ||
| if (typeMap != null && typeMap.MapExpression == null && _typeMapRegistry.GetTypeMap(typePair) == null) | ||
| { | ||
| lock (typeMap) | ||
| { | ||
| { | ||
| inlineConfiguration.Configure(typeMap); | ||
| typeMap.Seal(this); | ||
| } | ||
| } | ||
|
|
@@ -217,7 +222,10 @@ private TypeMap GetTypeMap(TypePair initialTypes) | |
| { | ||
| if (types != initialTypes && _typeMapPlanCache.TryGetValue(types, out var typeMap)) | ||
| { | ||
| return typeMap; | ||
| if (typeMap != null) | ||
| { | ||
| return typeMap; | ||
| } | ||
| } | ||
| typeMap = FindTypeMapFor(types); | ||
| if (typeMap != null) | ||
|
|
@@ -239,6 +247,16 @@ private TypeMap GetTypeMap(TypePair initialTypes) | |
| } | ||
| } | ||
|
|
||
| if (!hasMapper.Value | ||
| && Configuration.CreateMissingTypeMaps | ||
| && !(initialTypes.SourceType.IsAbstract() && initialTypes.SourceType.IsClass()) | ||
| && !(initialTypes.DestinationType.IsAbstract() && initialTypes.DestinationType.IsClass()) | ||
| && !ExcludedTypes.Contains(initialTypes.SourceType) | ||
| && !ExcludedTypes.Contains(initialTypes.DestinationType)) | ||
| { | ||
| return Configuration.CreateInlineMap(_typeMapRegistry, initialTypes); | ||
|
||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
|
|
@@ -405,7 +423,7 @@ private static Expression<UntypedMapperFunc> Wrap(MapRequest mapRequest, Delegat | |
| } | ||
| } | ||
|
|
||
| private class DefaultTypeMapConfig : ITypeMapConfiguration | ||
| internal class DefaultTypeMapConfig : ITypeMapConfiguration | ||
| { | ||
| public DefaultTypeMapConfig(TypePair types) | ||
| { | ||
|
|
@@ -414,7 +432,6 @@ public DefaultTypeMapConfig(TypePair types) | |
|
|
||
| public void Configure(TypeMap typeMap) { } | ||
|
|
||
| public MemberList MemberList => MemberList.Destination; | ||
| public Type SourceType => Types.SourceType; | ||
| public Type DestinationType => Types.DestinationType; | ||
| public bool IsOpenGeneric => false; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should lock here. It's difficult to write correct thread safe code in a lot of places. I think we should lock when calling CreateMap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already lock there but this is another place that's a static cache. Another option would be just to get rid of the cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was to not look for the attribute every time it tries to match the members to map. So it will be slower without the cache. But my point point is more general. A coarser grained lock would make the code easier to write and understand. That's why I added a global lock before each CreateMap. So we don't have to worry about concurrency at every point.