Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions AutoMapper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\ci.yml = .github\workflows\ci.yml
CONTRIBUTING.md = CONTRIBUTING.md
Directory.Build.props = Directory.Build.props
icon.png = icon.png
ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md
nuget.config = nuget.config
Push.ps1 = Push.ps1
Expand Down
20 changes: 20 additions & 0 deletions docs/12.0-Upgrade-Guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 12.0 Upgrade Guide

[Release notes](https://github.com/AutoMapper/AutoMapper/releases/tag/v12.0.0).

## Equivalent settings overwrite each other

That applies per map and also per member. For example, you can have only one type converter per map and only one resolver per member.

It might not be obvious that some settings are equivalent. For example, a value converter is a special kind of resolver, so a `ConvertUsing` will overwrite a `MapFrom`
for the same member.

You also cannot have for the same map/member separate configurations for `Map` and `ProjectTo`.

Another possible occurence is with `ForAllMaps` and `ForAllPropertyMaps` when it's possible to overwrite things already set in a particular map.

## `ResolutionContext.Options` was removed

You should use `ResolutionContext.Items` to access the items passed in the `Map` call.

Instead of `ServiceCtor` you should use dependency injection or pass the needed objects in the `Map` call.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ New to AutoMapper? Check out the :doc:`Getting-started` page first.
:caption: Upgrading

API-Changes
12.0-Upgrade-Guide
11.0-Upgrade-Guide
10.0-Upgrade-Guide
9.0-Upgrade-Guide
Expand Down
File renamed without changes.
File renamed without changes.
7 changes: 6 additions & 1 deletion src/AutoMapper/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ 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.
MembersMustExist : Member 'public AutoMapper.IMappingOperationOptions AutoMapper.ResolutionContext.Options.get()' 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 +14,7 @@ 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
MembersMustExist : Member 'public System.Linq.IQueryable<TDestination> AutoMapper.QueryableExtensions.Extensions.Map<TSource, TDestination>(System.Linq.IQueryable<TSource>, System.Linq.IQueryable<TDestination>, AutoMapper.IConfigurationProvider)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection<System.Reflection.MemberInfo> AutoMapper.QueryableExtensions.MemberVisitor.MemberPath.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'AutoMapper.QueryableExtensions.Impl.MemberAccessQueryMapperVisitor' does not exist in the implementation but it does exist in the contract.
Total Issues: 18
8 changes: 4 additions & 4 deletions src/AutoMapper/AutoMapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
</ItemGroup>

<ItemGroup>
<None Include="..\..\icon.png" Pack="true" PackagePath="" />
<None Include="..\..\README.md" Pack="true" PackagePath="" />
<None Include="..\..\icon.png" Pack="true" PackagePath="" Visible="False" />
<None Include="..\..\README.md" Pack="true" PackagePath="" Visible="False" />
</ItemGroup>

<ItemGroup>
Expand All @@ -44,8 +44,8 @@
</ItemGroup>

<Target Name="PreBuild" AfterTargets="GetAssemblyVersion">
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="powershell -ExecutionPolicy Unrestricted -File &quot;$(ProjectDir)\PreBuild.ps1&quot; -version $(Version)" />
<Exec Condition=" '$(OS)' != 'Windows_NT' " Command="&quot;$(ProjectDir)PreBuild.sh&quot; $(Version)" />
<Exec Condition=" '$(OS)' == 'Windows_NT' " Command="powershell -ExecutionPolicy Unrestricted -File &quot;$(ProjectDir)ApiCompat\PreBuild.ps1&quot; -version $(Version)" />
<Exec Condition=" '$(OS)' != 'Windows_NT' " Command="&quot;$(ProjectDir)ApiCompat/PreBuild.sh&quot; $(Version)" />
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using AutoMapper.Internal;
using System;
using System.Reflection;

namespace AutoMapper.Configuration.Annotations
{
Expand All @@ -17,6 +19,11 @@ public sealed class SourceMemberAttribute : Attribute, IMemberConfigurationProvi

public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
{
var destinationMember = memberConfigurationExpression.DestinationMember;
if (destinationMember.Has<ValueConverterAttribute>() || destinationMember.Has<ValueResolverAttribute>())
{
return;
}
memberConfigurationExpression.MapFrom(Name);
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/AutoMapper/Configuration/ConfigurationValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ private void DryRunTypeMap(ICollection<TypeMap> typeMapsChecked, TypePair types,
}

CheckPropertyMaps(typeMapsChecked, typeMap);
typeMap.IsValid = true;
}
else
{
Expand All @@ -143,7 +142,7 @@ private void CheckPropertyMaps(ICollection<TypeMap> typeMapsChecked, TypeMap typ
{
foreach (var memberMap in typeMap.MemberMaps)
{
if(memberMap.Ignored || memberMap.ValueConverterConfig != null || memberMap.ValueResolverConfig != null)
if(memberMap.Ignored)
{
continue;
}
Expand Down
10 changes: 5 additions & 5 deletions src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper.Internal;
using AutoMapper.Execution;
using AutoMapper.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand Down Expand Up @@ -51,12 +52,12 @@ public CtorParamConfigurationExpression(string ctorParamName, Type sourceType)
}

public void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember) =>
_ctorParamActions.Add(cpm => cpm.CustomMapExpression = sourceMember);
_ctorParamActions.Add(cpm => cpm.SetResolver(sourceMember));

public void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
{
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TMember>> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
_ctorParamActions.Add(cpm => cpm.CustomMapFunction = resolverExpression);
_ctorParamActions.Add(cpm => cpm.Resolver = new FuncResolver(resolverExpression));
}

public void MapFrom(string sourceMembersPath)
Expand All @@ -75,13 +76,12 @@ public void Configure(TypeMap typeMap)
var parameter = ctorMap[CtorParamName];
if (parameter == null)
{
throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}");
throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a matching constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}");
}
foreach (var action in _ctorParamActions)
{
action(parameter);
}
parameter.CanResolveValue = true;
}
}
}
2 changes: 1 addition & 1 deletion src/AutoMapper/Configuration/MapperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ TypeMap GetIncludedTypeMap(TypePair pair)
// we want the exact map the user included, but we could instantiate an open generic
if (typeMap?.Types != pair)
{
throw QueryMapperHelper.MissingMapException(pair);
throw TypeMap.MissingMapException(pair);
}
return typeMap;
}
Expand Down
78 changes: 18 additions & 60 deletions src/AutoMapper/Configuration/MappingExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ public IMappingExpression IncludeMembers(params string[] memberNames)
IncludedMembersNames = memberNames;
foreach(var memberName in memberNames)
{
SourceType.GetFieldOrProperty(memberName);
ForSourceMemberCore(memberName, o => o.DoNotValidate());
SourceType.GetFieldOrProperty(memberName);
}
TypeMapActions.Add(tm => tm.IncludedMembersNames = memberNames);
return this;
Expand Down Expand Up @@ -79,66 +78,32 @@ internal MemberConfigurationExpression ForMember(MemberInfo destinationProperty,

public class MemberConfigurationExpression : MemberConfigurationExpression<object, object, object>, IMemberConfigurationExpression
{
public MemberConfigurationExpression(MemberInfo destinationMember, Type sourceType)
: base(destinationMember, sourceType)
public MemberConfigurationExpression(MemberInfo destinationMember, Type sourceType) : base(destinationMember, sourceType)
{
}

public void MapFrom(Type valueResolverType)
{
var config = new ValueResolverConfiguration(valueResolverType, valueResolverType.GetGenericInterface(typeof(IValueResolver<,,>)));

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void MapFrom(Type valueResolverType, string sourceMemberName)
{
var config = new ValueResolverConfiguration(valueResolverType, valueResolverType.GetGenericInterface(typeof(IMemberValueResolver<,,,>)))
public void MapFrom(Type valueResolverType) => MapFromCore(new(valueResolverType, valueResolverType.GetGenericInterface(typeof(IValueResolver<,,>))));
public void MapFrom(Type valueResolverType, string sourceMemberName) =>
MapFromCore(new(valueResolverType, valueResolverType.GetGenericInterface(typeof(IMemberValueResolver<,,,>)))
{
SourceMemberName = sourceMemberName
};

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> resolver, string sourceMemberName)
{
var config = new ValueResolverConfiguration(resolver, typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember>))
});
public void MapFrom<TSource, TDestination, TSourceMember, TDestMember>(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember> resolver, string sourceMemberName) =>
MapFromCore(new(resolver, typeof(IMemberValueResolver<TSource, TDestination, TSourceMember, TDestMember>))
{
SourceMemberName = sourceMemberName
};

PropertyMapActions.Add(pm => pm.ValueResolverConfig = config);
}

public void ConvertUsing(Type valueConverterType)
=> PropertyMapActions.Add(pm => ConvertUsing(pm, valueConverterType));

public void ConvertUsing(Type valueConverterType, string sourceMemberName)
=> PropertyMapActions.Add(pm => ConvertUsing(pm, valueConverterType, sourceMemberName));

public void ConvertUsing<TSourceMember, TDestinationMember>(IValueConverter<TSourceMember, TDestinationMember> valueConverter, string sourceMemberName)
{
PropertyMapActions.Add(pm =>
});
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) =>
base.ConvertUsingCore(new(valueConverter, typeof(IValueConverter<TSourceMember, TDestinationMember>))
{
var config = new ValueResolverConfiguration(valueConverter, typeof(IValueConverter<TSourceMember, TDestinationMember>))
{
SourceMemberName = sourceMemberName
};

pm.ValueConverterConfig = config;
SourceMemberName = sourceMemberName
});
}

private static void ConvertUsing(PropertyMap propertyMap, Type valueConverterType, string sourceMemberName = null)
{
var config = new ValueResolverConfiguration(valueConverterType, valueConverterType.GetGenericInterface(typeof(IValueConverter<,>)))
private void ConvertUsingCore(Type valueConverterType, string sourceMemberName = null) =>
base.ConvertUsingCore(new(valueConverterType, valueConverterType.GetGenericInterface(typeof(IValueConverter<,>)))
{
SourceMemberName = sourceMemberName
};

propertyMap.ValueConverterConfig = config;
}
});
}
}

Expand Down Expand Up @@ -176,14 +141,7 @@ public IMappingExpression<TSource, TDestination> ForMember<TMember>(Expression<F
return ForDestinationMember(memberInfo, memberOptions);
}

private void IncludeMembersCore(LambdaExpression[] memberExpressions)
{
foreach(var member in memberExpressions.Select(memberExpression => memberExpression.GetMember()).Where(member => member != null))
{
ForSourceMemberCore(member, o => o.DoNotValidate());
}
TypeMapActions.Add(tm => tm.IncludedMembers = memberExpressions);
}
private void IncludeMembersCore(LambdaExpression[] memberExpressions) => TypeMapActions.Add(tm => tm.IncludedMembers = memberExpressions);

public IMappingExpression<TSource, TDestination> IncludeMembers(params Expression<Func<TSource, object>>[] memberExpressions)
{
Expand Down
Loading