Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9d2dfd9
consider member value resolvers and value converters for source valid…
lbargaoanu Jun 29, 2022
29f1beb
simplify
lbargaoanu Jun 29, 2022
8552e2e
re-enable configuration only tests
lbargaoanu Jun 30, 2022
18c7f73
try to cover all cases
lbargaoanu Jul 1, 2022
704e5ba
cosmetic
lbargaoanu Jul 6, 2022
ba60290
polymorphic implementation for type converters
lbargaoanu Jul 6, 2022
7eff43f
remove ConstructDestinationUsingServiceLocator
lbargaoanu Jul 7, 2022
3c2ec11
simplify
lbargaoanu Jul 7, 2022
884e22a
remove TypeMap.AsProxy
lbargaoanu Jul 7, 2022
a2c4ba0
remove DestinationTypeToUse
lbargaoanu Jul 7, 2022
42ef755
code reuse
lbargaoanu Jul 7, 2022
821d4fa
add MemberMap.Resolver
lbargaoanu Jul 8, 2022
c38b3dc
code reuse
lbargaoanu Jul 8, 2022
ed3ac6e
Resolver.GetSourceMember
lbargaoanu Jul 8, 2022
99092f4
ClassValueResolver
lbargaoanu Jul 8, 2022
2fa05d6
FuncResolver
lbargaoanu Jul 8, 2022
ce51167
ExpressionResolver
lbargaoanu Jul 8, 2022
8ce0961
remove storage for CustomCtorExpression
lbargaoanu Jul 8, 2022
40cecce
remove storage for TypeMap.CustomMapExpression
lbargaoanu Jul 8, 2022
6cd3631
cosmetic
lbargaoanu Jul 8, 2022
854fa31
implement the convention based resolver in MemberMap
lbargaoanu Jul 9, 2022
1eab5f1
implement CanResolveValue in MemberMap
lbargaoanu Jul 9, 2022
787ae9d
cosmetic
lbargaoanu Jul 9, 2022
9b872ef
remove TypeMap.IsValid
lbargaoanu Jul 10, 2022
63aeef3
DestinationTypeOverride is considered separately
lbargaoanu Jul 10, 2022
c366182
cosmetic
lbargaoanu Jul 10, 2022
8d87e4e
MemberMapDetails
lbargaoanu Jul 10, 2022
20e379d
TypeMapDetails
lbargaoanu Jul 10, 2022
a1c02c7
cosmetic
lbargaoanu Jul 10, 2022
415c1d4
remove TypeMap._orderedPropertyMaps
lbargaoanu Jul 11, 2022
8b733b1
cosmetic
lbargaoanu Jul 11, 2022
999d65f
reuse the constructor map object
lbargaoanu Jul 12, 2022
79fd635
cosmetic
lbargaoanu Jul 12, 2022
449a50b
remove QueryMapperVisitor
lbargaoanu Jul 13, 2022
5b0cf17
hard code the map based projection mappers
lbargaoanu Jul 14, 2022
6fdc82b
IPojectionMapper.IsMatch(TypePair context)
lbargaoanu Jul 15, 2022
ddc934f
cosmetic
lbargaoanu Jul 15, 2022
798ebf2
constructor parameters default values with ProjectTo
lbargaoanu Jul 17, 2022
6024cf3
remove ResolutionContext.Options
lbargaoanu Jul 18, 2022
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
Prev Previous commit
Next Next commit
ClassValueResolver
  • Loading branch information
lbargaoanu committed Jul 8, 2022
commit 99092f46b0296eb80d87265959754cf93a33d7d0
3 changes: 2 additions & 1 deletion src/AutoMapper/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
InterfacesShouldHaveSameMembers : Interface member 'public TMappingExpression AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'AutoMapper.ValueResolverConfiguration' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void AutoMapper.Configuration.MappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.IgnoreAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MapAtRuntimeAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
Expand All @@ -12,4 +13,4 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.UseExistingValueAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
Total Issues: 13
Total Issues: 14
2 changes: 1 addition & 1 deletion src/AutoMapper/Configuration/ConfigurationValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ private void CheckPropertyMaps(ICollection<TypeMap> typeMapsChecked, TypeMap typ
{
foreach (var memberMap in typeMap.MemberMaps)
{
if(memberMap.Ignored || memberMap.Resolver != null || memberMap.ValueResolverConfig != null)
if(memberMap.Ignored || memberMap.Resolver != null)
{
continue;
}
Expand Down
5 changes: 3 additions & 2 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,16 @@ public void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberVa
{
SourceMemberName = sourceMemberName
});
private void MapFromCore(ValueResolverConfiguration valueResolver) => PropertyMapActions.Add(pm => pm.ValueResolverConfig = valueResolver);
private void MapFromCore(ClassValueResolver valueResolver) => SetResolver(valueResolver);
private void SetResolver(ValueResolver valueResolver) => PropertyMapActions.Add(pm => pm.Resolver = valueResolver);
public void ConvertUsing(Type valueConverterType) => ConvertUsingCore(valueConverterType);
public void ConvertUsing(Type valueConverterType, string sourceMemberName) => ConvertUsingCore(valueConverterType, sourceMemberName);
public void ConvertUsing<TSourceMember, TDestinationMember>(IValueConverter<TSourceMember, TDestinationMember> valueConverter, string sourceMemberName) =>
ConvertUsingCore(new(valueConverter, typeof(IValueConverter<TSourceMember, TDestinationMember>))
{
SourceMemberName = sourceMemberName
});
private void ConvertUsingCore(ValueConverter converter) => PropertyMapActions.Add(pm => pm.Resolver = converter);
private void ConvertUsingCore(ValueConverter converter) => SetResolver(converter);
private void ConvertUsingCore(Type valueConverterType, string sourceMemberName = null) =>
ConvertUsingCore(new(valueConverterType, valueConverterType.GetGenericInterface(typeof(IValueConverter<,>)))
{
Expand Down
9 changes: 5 additions & 4 deletions src/AutoMapper/Configuration/MemberConfigurationExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public MemberConfigurationExpression(MemberInfo destinationMember, Type sourceTy
public void NullSubstitute(object nullSubstitute) => PropertyMapActions.Add(pm => pm.NullSubstitute = nullSubstitute);
public void MapFrom<TValueResolver>() where TValueResolver : IValueResolver<TSource, TDestination, TMember> =>
AddValueResolver(new(typeof(TValueResolver), typeof(IValueResolver<TSource, TDestination, TMember>)));
private void AddValueResolver(ValueResolverConfiguration config) => PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
private void AddValueResolver(ClassValueResolver config) => SetResolver(config);
private void SetResolver(ValueResolver config) => PropertyMapActions.Add(pm => pm.Resolver = config);
public void MapFrom<TValueResolver, TSourceMember>(Expression<Func<TSource, TSourceMember>> sourceMember)
where TValueResolver : IMemberValueResolver<TSource, TDestination, TSourceMember, TMember> =>
MapFromCore<TValueResolver, TSourceMember>(sourceMember);
Expand All @@ -41,14 +42,14 @@ private void MapFromCore<TValueResolver, TSourceMember>(Expression<Func<TSource,
AddValueResolver(new(typeof(TValueResolver), typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>))
{
SourceMemberName = sourceMemberName,
SourceMember = sourceMember
SourceMemberLambda = sourceMember
});
public void MapFrom(IValueResolver<TSource, TDestination, TMember> valueResolver) =>
AddValueResolver(new(valueResolver, typeof(IValueResolver<TSource, TDestination, TMember>)));
public void MapFrom<TSourceMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember> valueResolver, Expression<Func<TSource, TSourceMember>> sourceMember) =>
AddValueResolver(new(valueResolver, typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TMember>))
{
SourceMember = sourceMember
SourceMemberLambda = sourceMember
});
public void MapFrom<TResult>(Func<TSource, TDestination, TResult> mappingFunction) =>
MapFromResult((src, dest, destMember, ctxt) => mappingFunction(src, dest));
Expand Down Expand Up @@ -121,7 +122,7 @@ private void ConvertUsingCore<TValueConverter, TSourceMember>(Expression<Func<TS
SourceMemberLambda = sourceMember,
SourceMemberName = sourceMemberName
});
private void ConvertUsingCore(ValueConverter converter) => PropertyMapActions.Add(pm => pm.Resolver = converter);
private void ConvertUsingCore(ValueConverter converter) => SetResolver(converter);
private void ConvertUsingCore<TSourceMember>(IValueConverter<TSourceMember, TMember> valueConverter,
Expression<Func<TSource, TSourceMember>> sourceMember = null, string sourceMemberName = null) =>
ConvertUsingCore(new(valueConverter, typeof(IValueConverter<TSourceMember, TMember>))
Expand Down
74 changes: 39 additions & 35 deletions src/AutoMapper/Execution/TypeMapPlanBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,7 @@ private Expression BuildValueResolverFunc(MemberMap memberMap, Expression destVa
var destinationPropertyType = memberMap.DestinationType;
var valueResolverFunc = memberMap switch
{
{ Resolver: { } resolver } => resolver.GetExpression(memberMap, customSource, destValueExpr),
{ ValueResolverConfig: { } } => BuildResolveCall(memberMap, customSource, destValueExpr),
{ Resolver: { } resolver } => resolver.GetExpression(memberMap, customSource, _destination, destValueExpr),
{ CustomMapFunction: LambdaExpression function } => function.ConvertReplaceParameters(customSource, _destination, destValueExpr, ContextParameter),
{ CustomMapExpression: LambdaExpression mapFrom } => CustomMapExpression(mapFrom.ReplaceParameters(customSource), destinationPropertyType, destValueExpr),
{ SourceMembers.Length: > 0 } => memberMap.ChainSourceMembers(customSource, destinationPropertyType, destValueExpr),
Expand Down Expand Up @@ -412,56 +411,38 @@ static Expression CustomMapExpression(Expression mapFrom, Type destinationProper
}
}
private Expression GetCustomSource(MemberMap memberMap) => memberMap.IncludedMember?.Variable ?? Source;
private Expression BuildResolveCall(MemberMap memberMap, Expression source, Expression destValueExpr)
{
var typeMap = memberMap.TypeMap;
var valueResolverConfig = memberMap.ValueResolverConfig;
var resolverInstance = valueResolverConfig.Instance != null ?
Constant(valueResolverConfig.Instance) :
ServiceLocator(typeMap.MakeGenericType(valueResolverConfig.ConcreteType));
var sourceMember = valueResolverConfig.SourceMember?.ReplaceParameters(source) ??
(valueResolverConfig.SourceMemberName != null ? PropertyOrField(source, valueResolverConfig.SourceMemberName) : null);
var iResolverType = valueResolverConfig.InterfaceType;
if (iResolverType.ContainsGenericParameters)
{
var typeArgs =
iResolverType.GenericTypeArguments.Zip(new[] { typeMap.SourceType, typeMap.DestinationType, sourceMember?.Type, destValueExpr.Type }.Where(t => t != null),
(declaredType, runtimeType) => declaredType.ContainsGenericParameters ? runtimeType : declaredType).ToArray();
iResolverType = iResolverType.GetGenericTypeDefinition().MakeGenericType(typeArgs);
}
var parameters = new[] { source, _destination, sourceMember, destValueExpr }.Where(p => p != null)
.Zip(iResolverType.GenericTypeArguments, ToType)
.Append(ContextParameter)
.ToArray();
return Call(ToType(resolverInstance, iResolverType), "Resolve", parameters);
}
}
public abstract class ValueResolver
{
public abstract Expression GetExpression(MemberMap memberMap, Expression source, Expression destinationMember);
public abstract Expression GetExpression(MemberMap memberMap, Expression source, Expression destination, Expression destinationMember);
public abstract MemberInfo GetSourceMember(MemberMap memberMap);
public abstract Type ResolvedType { get; }
public abstract string SourceMemberName { get; set; }
}
public class ValueConverter : ValueResolver
public abstract class ValueResolverConfig : ValueResolver
{
readonly Expression _instance;
private protected Expression _instance;
public Type ConcreteType { get; }
public Type InterfaceType { get; }
public Type InterfaceType { get; protected set; }
public LambdaExpression SourceMemberLambda { get; set; }
public override string SourceMemberName { get; set; }
public ValueConverter(Type concreteType, Type interfaceType)
protected ValueResolverConfig(Type concreteType, Type interfaceType)
{
_instance = ServiceLocator(concreteType);
ConcreteType = concreteType;
InterfaceType = interfaceType;
}
public ValueConverter(object instance, Type interfaceType)
protected ValueResolverConfig(object instance, Type interfaceType)
{
_instance = Constant(instance);
InterfaceType = interfaceType;
}
public override Expression GetExpression(MemberMap memberMap, Expression source, Expression destinationMember)
public override string SourceMemberName { get; set; }
public override Type ResolvedType => InterfaceType.GenericTypeArguments.Last();
}
public class ValueConverter : ValueResolverConfig
{
public ValueConverter(Type concreteType, Type interfaceType) : base(concreteType, interfaceType) => _instance = ServiceLocator(concreteType);
public ValueConverter(object instance, Type interfaceType) : base(instance, interfaceType) { }
public override Expression GetExpression(MemberMap memberMap, Expression source, Expression destination, Expression destinationMember)
{
var iResolverTypeArgs = InterfaceType.GenericTypeArguments;
var sourceMember = SourceMemberLambda?.ReplaceParameters(source) ??
Expand All @@ -480,7 +461,30 @@ AutoMapperConfigurationException BuildExceptionMessage()
{ SourceMemberName: { } } => null,
_ => memberMap.SourceMembers.Length == 1 ? memberMap.SourceMembers[0] : null
};
public override Type ResolvedType => InterfaceType.GenericTypeArguments.Last();
}
public class ClassValueResolver : ValueResolverConfig
{
public ClassValueResolver(Type concreteType, Type interfaceType) : base(concreteType, interfaceType) { }
public ClassValueResolver(object instance, Type interfaceType) : base(instance, interfaceType) { }
public override Expression GetExpression(MemberMap memberMap, Expression source, Expression destination, Expression destinationMember)
{
var typeMap = memberMap.TypeMap;
var resolverInstance = _instance ?? ServiceLocator(typeMap.MakeGenericType(ConcreteType));
var sourceMember = SourceMemberLambda?.ReplaceParameters(source) ?? (SourceMemberName != null ? PropertyOrField(source, SourceMemberName) : null);
if (InterfaceType.ContainsGenericParameters)
{
var typeArgs =
InterfaceType.GenericTypeArguments.Zip(new[] { typeMap.SourceType, typeMap.DestinationType, sourceMember?.Type, destinationMember.Type }.Where(t => t != null),
(declaredType, runtimeType) => declaredType.ContainsGenericParameters ? runtimeType : declaredType).ToArray();
InterfaceType = InterfaceType.GetGenericTypeDefinition().MakeGenericType(typeArgs);
}
var parameters = new[] { source, destination, sourceMember, destinationMember }.Where(p => p != null)
.Zip(InterfaceType.GenericTypeArguments, ToType)
.Append(ContextParameter)
.ToArray();
return Call(ToType(resolverInstance, InterfaceType), "Resolve", parameters);
}
public override MemberInfo GetSourceMember(MemberMap memberMap) => SourceMemberLambda?.GetMember();
}
public abstract class TypeConverter
{
Expand Down
25 changes: 1 addition & 24 deletions src/AutoMapper/MemberMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,15 @@ public class MemberMap
public virtual LambdaExpression Condition { get => default; set { } }
public virtual LambdaExpression CustomMapFunction { get => default; set { } }
public ValueResolver Resolver { get; set; }
public virtual ValueResolverConfiguration ValueResolverConfig { get => default; set { } }
public virtual IReadOnlyCollection<ValueTransformerConfiguration> ValueTransformers => Array.Empty<ValueTransformerConfiguration>();
public MemberInfo SourceMember => this switch
{
{ Resolver: ValueResolver resolver } => resolver.GetSourceMember(this),
{ ValueResolverConfig.SourceMember: var sourceExpression } => sourceExpression?.GetMember(),
{ CustomMapFunction: { } } => null,
{ CustomMapExpression: LambdaExpression mapFrom } => mapFrom.GetMember(),
_ => SourceMembers.FirstOrDefault(),
};
public string GetSourceMemberName() => Resolver?.SourceMemberName ?? ValueResolverConfig?.SourceMemberName ?? SourceMember?.Name;
public string GetSourceMemberName() => Resolver?.SourceMemberName ?? SourceMember?.Name;
public bool MustUseDestination => UseDestinationValue is true || !CanBeSet;
public void MapFrom(LambdaExpression sourceMember)
{
Expand Down Expand Up @@ -80,27 +78,6 @@ public bool MapperEquals(MemberMap other)
}
public int MapperGetHashCode() => HashCode.Combine(MustUseDestination, MaxDepth, AllowsNullDestinationValues(), AllowsNullCollections());
}
public class ValueResolverConfiguration
{
public object Instance { get; }
public Type ConcreteType { get; }
public Type InterfaceType { get; }
public LambdaExpression SourceMember { get; set; }
public string SourceMemberName { get; set; }

public ValueResolverConfiguration(Type concreteType, Type interfaceType)
{
ConcreteType = concreteType;
InterfaceType = interfaceType;
}

public ValueResolverConfiguration(object instance, Type interfaceType)
{
Instance = instance;
InterfaceType = interfaceType;
}
public Type ResolvedType => InterfaceType.GenericTypeArguments.Last();
}
public readonly struct ValueTransformerConfiguration
{
public readonly Type ValueType;
Expand Down
5 changes: 1 addition & 4 deletions src/AutoMapper/PropertyMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,11 @@ public PropertyMap(PropertyMap includedMemberMap, TypeMap typeMap, IncludedMembe
public override bool? UseDestinationValue { get; set; }
public bool? ExplicitExpansion { get; set; }
public override object NullSubstitute { get; set; }
public override ValueResolverConfiguration ValueResolverConfig { get; set; }
public override IReadOnlyCollection<ValueTransformerConfiguration> ValueTransformers => _valueTransformerConfigs.NullCheck();
public override Type SourceType
{
get => _sourceType ??=
Resolver?.ResolvedType ??
ValueResolverConfig?.ResolvedType ??
CustomMapFunction?.ReturnType ??
CustomMapExpression?.ReturnType ??
(_sourceMembers.Length > 0 ? _sourceMembers[_sourceMembers.Length - 1].GetMemberType() : typeof(object));
Expand All @@ -73,7 +71,6 @@ public void ApplyInheritedPropertyMap(PropertyMap inheritedMappedProperty)
_sourceType = inheritedMappedProperty._sourceType;
CustomMapExpression = inheritedMappedProperty.CustomMapExpression;
CustomMapFunction = inheritedMappedProperty.CustomMapFunction;
ValueResolverConfig = inheritedMappedProperty.ValueResolverConfig;
Resolver = inheritedMappedProperty.Resolver;
}
else if (_sourceMembers.Length == 0)
Expand All @@ -96,7 +93,7 @@ public void ApplyInheritedPropertyMap(PropertyMap inheritedMappedProperty)
}
}
public override bool CanResolveValue => _canResolveValue ??= !Ignored && (_sourceMembers.Length > 0 || IsResolveConfigured);
public bool IsResolveConfigured => (ValueResolverConfig ?? CustomMapFunction ?? CustomMapExpression ?? (object)Resolver) != null;
public bool IsResolveConfigured => (CustomMapFunction ?? CustomMapExpression ?? (object)Resolver) != null;
public void AddValueTransformation(ValueTransformerConfiguration valueTransformerConfiguration)
{
_valueTransformerConfigs ??= new();
Expand Down