Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AutoMapper/AutoMapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<Summary>A convention-based object-object mapper</Summary>
<Description>A convention-based object-object mapper. AutoMapper uses a fluent configuration API to define an object-object mapping strategy. AutoMapper uses a convention-based matching algorithm to match up source to destination values. Currently, AutoMapper is designed for model projection scenarios to flatten complex object models to DTOs and other simple objects, whose design is better suited for serialization, communication, messaging, or simply an anti-corruption layer between the domain and application layer.</Description>
<VersionPrefix>6.2.0</VersionPrefix>
<VersionPrefix>6.2.1</VersionPrefix>
<Authors>Jimmy Bogard</Authors>
<TargetFrameworks>netstandard1.3;netstandard1.1;net45;net40</TargetFrameworks>
<NoWarn>$(NoWarn);1591</NoWarn>
Expand Down
1 change: 0 additions & 1 deletion src/AutoMapper/Configuration/ITypeMapConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace AutoMapper.Configuration
public interface ITypeMapConfiguration
{
void Configure(TypeMap typeMap);
MemberList MemberList { get; }
Type SourceType { get; }
Type DestinationType { get; }
bool IsOpenGeneric { get; }
Expand Down
3 changes: 1 addition & 2 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,11 @@ public MappingExpression(MemberList memberList, Type sourceType, Type destinatio

public MappingExpression(MemberList memberList, TypePair types)
{
MemberList = memberList;
Types = types;
IsOpenGeneric = types.SourceType.IsGenericTypeDefinition() || types.DestinationType.IsGenericTypeDefinition();
TypeMapActions.Add(tm => tm.ConfiguredMemberList = memberList);
}

public MemberList MemberList { get; }
public TypePair Types { get; }
public Type SourceType => Types.SourceType;
public Type DestinationType => Types.DestinationType;
Expand Down
17 changes: 17 additions & 0 deletions src/AutoMapper/IConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ public interface IConfigurationProvider
/// <returns>Type map configuration</returns>
TypeMap ResolveTypeMap(Type sourceType, Type destinationType);

/// <summary>
/// Resolve the <see cref="TypeMap"/> for the configured source and destination type, checking parent types
/// </summary>
/// <param name="sourceType">Configured source type</param>
/// <param name="destinationType">Configured destination type</param>
/// <param name="inlineConfiguration">Inline type map configuration if exists</param>
/// <returns>Type map configuration</returns>
TypeMap ResolveTypeMap(Type sourceType, Type destinationType, ITypeMapConfiguration inlineConfiguration);

/// <summary>
/// Resolve the <see cref="TypeMap"/> for the configured type pair, checking parent types
/// </summary>
/// <param name="typePair">Type pair</param>
/// <param name="inlineConfiguration">Inline type map configuration if exists</param>
/// <returns>Type map configuration</returns>
TypeMap ResolveTypeMap(TypePair typePair, ITypeMapConfiguration inlineConfiguration);

/// <summary>
/// Resolve the <see cref="TypeMap"/> for the configured type pair, checking parent types
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/AutoMapper/LockingConcurrentDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public LockingConcurrentDictionary(Func<TKey, TValue> valueFactory)
}

public TValue GetOrAdd(TKey key) => _dictionary.GetOrAdd(key, _valueFactory).Value;
public TValue GetOrAdd(TKey key, Func<TKey, Lazy<TValue>> valueFactory) => _dictionary.GetOrAdd(key, valueFactory).Value;

public TValue this[TKey key]
{
Expand Down
53 changes: 37 additions & 16 deletions src/AutoMapper/MapperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -193,31 +187,46 @@ 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);
}
}
return typeMap;
}

private TypeMap GetTypeMap(TypePair initialTypes)
{
var hasMapper = new Lazy<bool>(() => FindMapper(initialTypes) != null);
{
var doesNotHaveMapper = FindMapper(initialTypes) == null;

foreach (var types in initialTypes.GetRelatedTypePairs())
{
if (types != initialTypes && _typeMapPlanCache.TryGetValue(types, out var typeMap))
{
return typeMap;
if (typeMap != null)
{
return typeMap;
}
}
typeMap = FindTypeMapFor(types);
if (typeMap != null)
Expand All @@ -229,7 +238,7 @@ private TypeMap GetTypeMap(TypePair initialTypes)
{
return typeMap;
}
if (!hasMapper.Value)
if (doesNotHaveMapper)
{
typeMap = FindConventionTypeMapFor(types);
if (typeMap != null)
Expand All @@ -239,6 +248,19 @@ private TypeMap GetTypeMap(TypePair initialTypes)
}
}

if (doesNotHaveMapper
&& Configuration.CreateMissingTypeMaps
&& !(initialTypes.SourceType.IsAbstract() && initialTypes.SourceType.IsClass())
&& !(initialTypes.DestinationType.IsAbstract() && initialTypes.DestinationType.IsClass())
&& !ExcludedTypes.Contains(initialTypes.SourceType)
&& !ExcludedTypes.Contains(initialTypes.DestinationType))
{
lock (this)
{
return Configuration.CreateInlineMap(_typeMapRegistry, initialTypes);
}
}

return null;
}

Expand Down Expand Up @@ -405,7 +427,7 @@ private static Expression<UntypedMapperFunc> Wrap(MapRequest mapRequest, Delegat
}
}

private class DefaultTypeMapConfig : ITypeMapConfiguration
internal class DefaultTypeMapConfig : ITypeMapConfiguration
{
public DefaultTypeMapConfig(TypePair types)
{
Expand All @@ -414,7 +436,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;
Expand Down
13 changes: 5 additions & 8 deletions src/AutoMapper/ProfileMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ struct IConvertible{}
[DebuggerDisplay("{Name}")]
public class ProfileMap
{
private static readonly Type[] ExcludedTypes = { typeof(object), typeof(ValueType), typeof(Enum), typeof(IComparable), typeof(IFormattable), typeof(IConvertible) };
private readonly TypeMapFactory _typeMapFactory = new TypeMapFactory();
private readonly IEnumerable<ITypeMapConfiguration> _typeMapConfigs;
private readonly IEnumerable<ITypeMapConfiguration> _openTypeMapConfigs;
Expand Down Expand Up @@ -129,7 +128,7 @@ public void Configure(TypeMapRegistry typeMapRegistry)

private void BuildTypeMap(TypeMapRegistry typeMapRegistry, ITypeMapConfiguration config)
{
var typeMap = _typeMapFactory.CreateTypeMap(config.SourceType, config.DestinationType, this, config.MemberList);
var typeMap = _typeMapFactory.CreateTypeMap(config.SourceType, config.DestinationType, this);

config.Configure(typeMap);

Expand Down Expand Up @@ -176,7 +175,7 @@ public bool IsConventionMap(TypePair types)

public TypeMap CreateConventionTypeMap(TypeMapRegistry typeMapRegistry, TypePair types)
{
var typeMap = _typeMapFactory.CreateTypeMap(types.SourceType, types.DestinationType, this, MemberList.Destination);
var typeMap = _typeMapFactory.CreateTypeMap(types.SourceType, types.DestinationType, this);

typeMap.IsConventionMap = true;

Expand All @@ -189,22 +188,20 @@ public TypeMap CreateConventionTypeMap(TypeMapRegistry typeMapRegistry, TypePair
return typeMap;
}

public TypeMap CreateInlineMap(TypeMapRegistry typeMapRegistry, ITypeMapConfiguration inlineConfig)
public TypeMap CreateInlineMap(TypeMapRegistry typeMapRegistry, TypePair types)
{
var typeMap = _typeMapFactory.CreateTypeMap(inlineConfig.SourceType, inlineConfig.DestinationType, this, inlineConfig.MemberList);
var typeMap = _typeMapFactory.CreateTypeMap(types.SourceType, types.DestinationType, this);

typeMap.IsConventionMap = true;

inlineConfig.Configure(typeMap);

Configure(typeMapRegistry, typeMap);

return typeMap;
}

public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, TypeMapRegistry typeMapRegistry, TypePair closedTypes)
{
var closedMap = _typeMapFactory.CreateTypeMap(closedTypes.SourceType, closedTypes.DestinationType, this, openMapConfig.MemberList);
var closedMap = _typeMapFactory.CreateTypeMap(closedTypes.SourceType, closedTypes.DestinationType, this);

openMapConfig.Configure(closedMap);

Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/QueryableExtensions/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ public override QueryExpressions GetSubQueryExpression(ExpressionBuilder builder
TypeMap firstTypeMap;
lock(_configurationProvider)
{
firstTypeMap = typeMapFactory.CreateTypeMap(request.SourceType, letType, typeMap.Profile, MemberList.None);
firstTypeMap = typeMapFactory.CreateTypeMap(request.SourceType, letType, typeMap.Profile);
}
var secondParameter = Parameter(letType, "dto");

Expand Down
5 changes: 2 additions & 3 deletions src/AutoMapper/TypeMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@ public class TypeMap
private readonly IList<TypeMap> _inheritedTypeMaps = new List<TypeMap>();
private readonly List<ValueTransformerConfiguration> _valueTransformerConfigs = new List<ValueTransformerConfiguration>();

public TypeMap(TypeDetails sourceType, TypeDetails destinationType, MemberList memberList, ProfileMap profile)
public TypeMap(TypeDetails sourceType, TypeDetails destinationType, ProfileMap profile)
{
SourceTypeDetails = sourceType;
DestinationTypeDetails = destinationType;
Types = new TypePair(sourceType.Type, destinationType.Type);
Profile = profile;
ConfiguredMemberList = memberList;
}

public PathMap FindOrCreatePathMapFor(LambdaExpression destinationExpression, MemberPath path, TypeMap typeMap)
Expand Down Expand Up @@ -77,7 +76,7 @@ public PathMap FindPathMapByDestinationPath(string destinationFullPath) =>

public bool ConstructDestinationUsingServiceLocator { get; set; }

public MemberList ConfiguredMemberList { get; }
public MemberList ConfiguredMemberList { get; set; }

public IEnumerable<TypePair> IncludedDerivedTypes => _includedDerivedTypes;
public IEnumerable<TypePair> IncludedBaseTypes => _includedBaseTypes;
Expand Down
4 changes: 2 additions & 2 deletions src/AutoMapper/TypeMapFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ namespace AutoMapper
{
public class TypeMapFactory
{
public TypeMap CreateTypeMap(Type sourceType, Type destinationType, ProfileMap options, MemberList memberList)
public TypeMap CreateTypeMap(Type sourceType, Type destinationType, ProfileMap options)
{
var sourceTypeInfo = options.CreateTypeDetails(sourceType);
var destTypeInfo = options.CreateTypeDetails(destinationType);

var typeMap = new TypeMap(sourceTypeInfo, destTypeInfo, memberList, options);
var typeMap = new TypeMap(sourceTypeInfo, destTypeInfo, options);

foreach (var destProperty in destTypeInfo.PublicWriteAccessors)
{
Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/TypePair.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public struct MapRequest : IEquatable<MapRequest>
public ITypeMapConfiguration InlineConfig { get; }

public MapRequest(TypePair requestedTypes, TypePair runtimeTypes)
: this(requestedTypes, runtimeTypes, null)
: this(requestedTypes, runtimeTypes, new MapperConfiguration.DefaultTypeMapConfig(requestedTypes))
{
}

Expand Down
7 changes: 4 additions & 3 deletions src/UnitTests/Bug/BaseMapWithIncludesAndUnincludedMappings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ public class Container2
{
public BaseB Item { get; set; }
}
public class BaseA
public abstract class BaseA
{
public string Name { get; set; }
}

public class BaseB
public abstract class BaseB
{
public string Name { get; set; }
}
Expand Down Expand Up @@ -84,7 +84,8 @@ public void TestInitialiserProxyOfSub()
cfg.CreateMap<Container, Container2>();
});

var mapped = config.CreateMapper().Map<Container, Container2>(new Container() { Item = new ProxyOfSubA() { Name = "Martin", Description = "Hello" } });
var mapped = config.CreateMapper()
.Map<Container, Container2>(new Container() { Item = new ProxyOfSubA() { Name = "Martin", Description = "Hello" } });
Assert.IsType<SubB>(mapped.Item);

}
Expand Down
1 change: 1 addition & 0 deletions src/UnitTests/Bug/ExpressionMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public Parent Parent

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = false;
cfg.CreateMap<GrandParent, GrandParentDTO>().ReverseMap();
cfg.CreateMap<Parent, ParentDTO>().ReverseMap();
cfg.CreateMap<Child, ChildDTO>()
Expand Down
27 changes: 27 additions & 0 deletions src/UnitTests/DynamicMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Xunit;
using Shouldly;
using System.Collections.Generic;
using AutoMapper.QueryableExtensions;

namespace AutoMapper.UnitTests.DynamicMapping
{
Expand Down Expand Up @@ -396,6 +397,32 @@ public void Should_map()
}
}

public class When_automatically_dynamically_projecting : NonValidatingSpecBase
{
public class Source
{
public int Value { get; set; }
}

public class Dest
{
public int Value { get; set; }
}

protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => {});

[Fact]
public void Should_map()
{
var source = new Source {Value = 5};
var items = new[] {source}.AsQueryable();
var dest = items.ProjectTo<Dest>(ConfigProvider).ToArray();

dest.Length.ShouldBe(1);
dest[0].Value.ShouldBe(5);
}
}

public class When_mixing_auto_and_manual_map : NonValidatingSpecBase
{
public class Source
Expand Down
1 change: 1 addition & 0 deletions src/UnitTests/Projection/ProjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public ProjectTest()
{
cfg.CreateMap<Address, AddressDto>();
cfg.CreateMap<Customer, CustomerDto>();
cfg.CreateMissingTypeMaps = false;
});
}

Expand Down
1 change: 1 addition & 0 deletions src/UnitTests/Query/SourceInjectedQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ private static IMapper SetupAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = false;
cfg.CreateMap<User, UserModel>()
.ForMember(d => d.Id, opt => opt.MapFrom(s => s.UserId))
.ForMember(d => d.FullName, opt => opt.MapFrom(s => s.Name))
Expand Down
Loading