tools/AutoMapper/ProfileMap.cs (208 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using AutoMapper.Configuration;
using AutoMapper.Configuration.Conventions;
using AutoMapper.Mappers;
namespace AutoMapper
{
#if NETSTANDARD1_1
struct IConvertible{}
#endif
[DebuggerDisplay("{Name}")]
public class ProfileMap
{
private readonly TypeMapFactory _typeMapFactory = new TypeMapFactory();
private readonly IEnumerable<ITypeMapConfiguration> _typeMapConfigs;
private readonly IEnumerable<ITypeMapConfiguration> _openTypeMapConfigs;
private readonly LockingConcurrentDictionary<Type, TypeDetails> _typeDetails;
public ProfileMap(IProfileConfiguration profile)
: this(profile, null)
{
}
public ProfileMap(IProfileConfiguration profile, IConfiguration configuration)
{
_typeDetails = new LockingConcurrentDictionary<Type, TypeDetails>(TypeDetailsFactory);
Name = profile.ProfileName;
AllowNullCollections = profile.AllowNullCollections ?? configuration?.AllowNullCollections ?? false;
AllowNullDestinationValues = profile.AllowNullDestinationValues ?? configuration?.AllowNullDestinationValues ?? true;
EnableNullPropagationForQueryMapping = profile.EnableNullPropagationForQueryMapping ?? configuration?.EnableNullPropagationForQueryMapping ?? false;
ConstructorMappingEnabled = profile.ConstructorMappingEnabled ?? configuration?.ConstructorMappingEnabled ?? true;
ShouldMapField = profile.ShouldMapField ?? configuration?.ShouldMapField ?? (p => p.IsPublic());
ShouldMapProperty = profile.ShouldMapProperty ?? configuration?.ShouldMapProperty ?? (p => p.IsPublic());
CreateMissingTypeMaps = profile.CreateMissingTypeMaps ?? configuration?.CreateMissingTypeMaps ?? true;
ValidateInlineMaps = profile.ValidateInlineMaps ?? configuration?.ValidateInlineMaps ?? true;
TypeConfigurations = profile.TypeConfigurations
.Concat(configuration?.TypeConfigurations ?? Enumerable.Empty<IConditionalObjectMapper>())
.ToArray();
ValueTransformers = profile.ValueTransformers.Concat(configuration?.ValueTransformers ?? Enumerable.Empty<ValueTransformerConfiguration>()).ToArray();
MemberConfigurations = profile.MemberConfigurations.ToArray();
MemberConfigurations.FirstOrDefault()?.AddMember<NameSplitMember>(_ => _.SourceMemberNamingConvention = profile.SourceMemberNamingConvention);
MemberConfigurations.FirstOrDefault()?.AddMember<NameSplitMember>(_ => _.DestinationMemberNamingConvention = profile.DestinationMemberNamingConvention);
GlobalIgnores = profile.GlobalIgnores.Concat(configuration?.GlobalIgnores ?? Enumerable.Empty<string>()).ToArray();
SourceExtensionMethods = profile.SourceExtensionMethods.Concat(configuration?.SourceExtensionMethods ?? Enumerable.Empty<MethodInfo>()).ToArray();
AllPropertyMapActions = profile.AllPropertyMapActions.Concat(configuration?.AllPropertyMapActions ?? Enumerable.Empty<Action<PropertyMap, IMemberConfigurationExpression>>()).ToArray();
AllTypeMapActions = profile.AllTypeMapActions.Concat(configuration?.AllTypeMapActions ?? Enumerable.Empty<Action<TypeMap, IMappingExpression>>()).ToArray();
Prefixes =
profile.MemberConfigurations
.Select(m => m.NameMapper)
.SelectMany(m => m.NamedMappers)
.OfType<PrePostfixName>()
.SelectMany(m => m.Prefixes)
.ToArray();
Postfixes =
profile.MemberConfigurations
.Select(m => m.NameMapper)
.SelectMany(m => m.NamedMappers)
.OfType<PrePostfixName>()
.SelectMany(m => m.Postfixes)
.ToArray();
_typeMapConfigs = profile.TypeMapConfigs.ToArray();
_openTypeMapConfigs = profile.OpenTypeMapConfigs.ToArray();
}
public bool AllowNullCollections { get; }
public bool AllowNullDestinationValues { get; }
public bool ConstructorMappingEnabled { get; }
public bool CreateMissingTypeMaps { get; }
public bool ValidateInlineMaps { get; }
public bool EnableNullPropagationForQueryMapping { get; }
public string Name { get; }
public Func<FieldInfo, bool> ShouldMapField { get; }
public Func<PropertyInfo, bool> ShouldMapProperty { get; }
public IEnumerable<Action<PropertyMap, IMemberConfigurationExpression>> AllPropertyMapActions { get; }
public IEnumerable<Action<TypeMap, IMappingExpression>> AllTypeMapActions { get; }
public IEnumerable<string> GlobalIgnores { get; }
public IEnumerable<IMemberConfiguration> MemberConfigurations { get; }
public IEnumerable<MethodInfo> SourceExtensionMethods { get; }
public IEnumerable<IConditionalObjectMapper> TypeConfigurations { get; }
public IEnumerable<string> Prefixes { get; }
public IEnumerable<string> Postfixes { get; }
public IEnumerable<ValueTransformerConfiguration> ValueTransformers { get; }
public TypeDetails CreateTypeDetails(Type type) => _typeDetails.GetOrAdd(type);
private TypeDetails TypeDetailsFactory(Type type) => new TypeDetails(type, this);
public void Register(TypeMapRegistry typeMapRegistry)
{
foreach (var config in _typeMapConfigs.Where(c => !c.IsOpenGeneric))
{
BuildTypeMap(typeMapRegistry, config);
if (config.ReverseTypeMap != null)
{
BuildTypeMap(typeMapRegistry, config.ReverseTypeMap);
}
}
}
public void Configure(TypeMapRegistry typeMapRegistry)
{
foreach (var typeMapConfiguration in _typeMapConfigs.Where(c => !c.IsOpenGeneric))
{
Configure(typeMapRegistry, typeMapConfiguration);
if (typeMapConfiguration.ReverseTypeMap != null)
{
Configure(typeMapRegistry, typeMapConfiguration.ReverseTypeMap);
}
}
}
private void BuildTypeMap(TypeMapRegistry typeMapRegistry, ITypeMapConfiguration config)
{
var typeMap = _typeMapFactory.CreateTypeMap(config.SourceType, config.DestinationType, this);
config.Configure(typeMap);
typeMapRegistry.RegisterTypeMap(typeMap);
}
private void Configure(TypeMapRegistry typeMapRegistry, ITypeMapConfiguration typeMapConfiguration)
{
var typeMap = typeMapRegistry.GetTypeMap(typeMapConfiguration.Types);
Configure(typeMapRegistry, typeMap);
}
private void Configure(TypeMapRegistry typeMapRegistry, TypeMap typeMap)
{
foreach (var action in AllTypeMapActions)
{
var expression = new MappingExpression(typeMap.Types, typeMap.ConfiguredMemberList);
action(typeMap, expression);
expression.Configure(typeMap);
}
foreach (var action in AllPropertyMapActions)
{
foreach (var propertyMap in typeMap.GetPropertyMaps())
{
var memberExpression = new MappingExpression.MemberConfigurationExpression(propertyMap.DestinationProperty, typeMap.SourceType);
action(propertyMap, memberExpression);
memberExpression.Configure(typeMap);
}
}
ApplyBaseMaps(typeMapRegistry, typeMap, typeMap);
ApplyDerivedMaps(typeMapRegistry, typeMap, typeMap);
}
public bool IsConventionMap(TypePair types)
{
return TypeConfigurations.Any(c => c.IsMatch(types));
}
public TypeMap CreateConventionTypeMap(TypeMapRegistry typeMapRegistry, TypePair types)
{
var typeMap = _typeMapFactory.CreateTypeMap(types.SourceType, types.DestinationType, this);
typeMap.IsConventionMap = true;
var config = new MappingExpression(typeMap.Types, typeMap.ConfiguredMemberList);
config.Configure(typeMap);
Configure(typeMapRegistry, typeMap);
return typeMap;
}
public TypeMap CreateInlineMap(TypeMapRegistry typeMapRegistry, TypePair types)
{
var typeMap = _typeMapFactory.CreateTypeMap(types.SourceType, types.DestinationType, this);
typeMap.IsConventionMap = true;
Configure(typeMapRegistry, typeMap);
return typeMap;
}
public TypeMap CreateClosedGenericTypeMap(ITypeMapConfiguration openMapConfig, TypeMapRegistry typeMapRegistry, TypePair closedTypes)
{
var closedMap = _typeMapFactory.CreateTypeMap(closedTypes.SourceType, closedTypes.DestinationType, this);
openMapConfig.Configure(closedMap);
Configure(typeMapRegistry, closedMap);
if(closedMap.TypeConverterType != null)
{
var typeParams =
(openMapConfig.SourceType.IsGenericTypeDefinition() ? closedTypes.SourceType.GetGenericArguments() : new Type[0])
.Concat
(openMapConfig.DestinationType.IsGenericTypeDefinition() ? closedTypes.DestinationType.GetGenericArguments() : new Type[0]);
var neededParameters = closedMap.TypeConverterType.GetGenericParameters().Length;
closedMap.TypeConverterType = closedMap.TypeConverterType.MakeGenericType(typeParams.Take(neededParameters).ToArray());
}
if(closedMap.DestinationTypeOverride?.IsGenericTypeDefinition() == true)
{
var neededParameters = closedMap.DestinationTypeOverride.GetGenericParameters().Length;
closedMap.DestinationTypeOverride = closedMap.DestinationTypeOverride.MakeGenericType(closedTypes.DestinationType.GetGenericArguments().Take(neededParameters).ToArray());
}
return closedMap;
}
public ITypeMapConfiguration GetGenericMap(TypePair closedTypes)
{
return _openTypeMapConfigs
.SelectMany(tm => tm.ReverseTypeMap == null ? new[] { tm } : new[] { tm, tm.ReverseTypeMap })
.Where(tm =>
tm.Types.SourceType.GetGenericTypeDefinitionIfGeneric() == closedTypes.SourceType.GetGenericTypeDefinitionIfGeneric() &&
tm.Types.DestinationType.GetGenericTypeDefinitionIfGeneric() == closedTypes.DestinationType.GetGenericTypeDefinitionIfGeneric())
.OrderByDescending(tm => tm.DestinationType == closedTypes.DestinationType) // Favor more specific destination matches,
.ThenByDescending(tm => tm.SourceType == closedTypes.SourceType) // then more specific source matches
.FirstOrDefault();
}
private static void ApplyBaseMaps(TypeMapRegistry typeMapRegistry, TypeMap derivedMap, TypeMap currentMap)
{
foreach (var baseMap in currentMap.IncludedBaseTypes.Select(typeMapRegistry.GetTypeMap).Where(baseMap => baseMap != null))
{
baseMap.IncludeDerivedTypes(currentMap.SourceType, currentMap.DestinationType);
derivedMap.AddInheritedMap(baseMap);
ApplyBaseMaps(typeMapRegistry, derivedMap, baseMap);
}
}
private void ApplyDerivedMaps(TypeMapRegistry typeMapRegistry, TypeMap baseMap, TypeMap typeMap)
{
foreach (var inheritedTypeMap in typeMap.IncludedDerivedTypes.Select(typeMapRegistry.GetTypeMap).Where(map => map != null))
{
inheritedTypeMap.AddInheritedMap(baseMap);
ApplyDerivedMaps(typeMapRegistry, baseMap, inheritedTypeMap);
}
}
}
}