Skip to content

Commit b8f8546

Browse files
authored
Merge pull request #4011 from AutoMapper/source_validation
Consider member value resolvers and value converters for source valid…
2 parents e39ecca + 6024cf3 commit b8f8546

File tree

75 files changed

+1046
-1378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1046
-1378
lines changed

AutoMapper.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
99
.github\workflows\ci.yml = .github\workflows\ci.yml
1010
CONTRIBUTING.md = CONTRIBUTING.md
1111
Directory.Build.props = Directory.Build.props
12+
icon.png = icon.png
1213
ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md
1314
nuget.config = nuget.config
1415
Push.ps1 = Push.ps1

docs/12.0-Upgrade-Guide.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# 12.0 Upgrade Guide
2+
3+
[Release notes](https://github.com/AutoMapper/AutoMapper/releases/tag/v12.0.0).
4+
5+
## Equivalent settings overwrite each other
6+
7+
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.
8+
9+
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`
10+
for the same member.
11+
12+
You also cannot have for the same map/member separate configurations for `Map` and `ProjectTo`.
13+
14+
Another possible occurence is with `ForAllMaps` and `ForAllPropertyMaps` when it's possible to overwrite things already set in a particular map.
15+
16+
## `ResolutionContext.Options` was removed
17+
18+
You should use `ResolutionContext.Items` to access the items passed in the `Map` call.
19+
20+
Instead of `ServiceCtor` you should use dependency injection or pass the needed objects in the `Map` call.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ New to AutoMapper? Check out the :doc:`Getting-started` page first.
7272
:caption: Upgrading
7373

7474
API-Changes
75+
12.0-Upgrade-Guide
7576
11.0-Upgrade-Guide
7677
10.0-Upgrade-Guide
7778
9.0-Upgrade-Guide
File renamed without changes.
File renamed without changes.

src/AutoMapper/ApiCompatBaseline.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
33
InterfacesShouldHaveSameMembers : Interface member 'public TMappingExpression AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the implementation but not in the contract.
44
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' is present in the contract but not in the implementation.
55
MembersMustExist : Member 'public void AutoMapper.IMappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
6+
MembersMustExist : Member 'public AutoMapper.IMappingOperationOptions AutoMapper.ResolutionContext.Options.get()' does not exist in the implementation but it does exist in the contract.
7+
TypesMustExist : Type 'AutoMapper.ValueResolverConfiguration' does not exist in the implementation but it does exist in the contract.
68
MembersMustExist : Member 'public void AutoMapper.Configuration.MappingExpressionBase<TSource, TDestination, TMappingExpression>.AsProxy()' does not exist in the implementation but it does exist in the contract.
79
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.
810
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.
@@ -12,4 +14,7 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
1214
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.
1315
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.
1416
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.
15-
Total Issues: 13
17+
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.
18+
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.
19+
TypesMustExist : Type 'AutoMapper.QueryableExtensions.Impl.MemberAccessQueryMapperVisitor' does not exist in the implementation but it does exist in the contract.
20+
Total Issues: 18

src/AutoMapper/AutoMapper.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
</ItemGroup>
3333

3434
<ItemGroup>
35-
<None Include="..\..\icon.png" Pack="true" PackagePath="" />
36-
<None Include="..\..\README.md" Pack="true" PackagePath="" />
35+
<None Include="..\..\icon.png" Pack="true" PackagePath="" Visible="False" />
36+
<None Include="..\..\README.md" Pack="true" PackagePath="" Visible="False" />
3737
</ItemGroup>
3838

3939
<ItemGroup>
@@ -44,8 +44,8 @@
4444
</ItemGroup>
4545

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

5151
</Project>

src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
using AutoMapper.Internal;
2+
using System;
3+
using System.Reflection;
24

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

1820
public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
1921
{
22+
var destinationMember = memberConfigurationExpression.DestinationMember;
23+
if (destinationMember.Has<ValueConverterAttribute>() || destinationMember.Has<ValueResolverAttribute>())
24+
{
25+
return;
26+
}
2027
memberConfigurationExpression.MapFrom(Name);
2128
}
2229
}

src/AutoMapper/Configuration/ConfigurationValidator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ private void DryRunTypeMap(ICollection<TypeMap> typeMapsChecked, TypePair types,
117117
}
118118

119119
CheckPropertyMaps(typeMapsChecked, typeMap);
120-
typeMap.IsValid = true;
121120
}
122121
else
123122
{
@@ -143,7 +142,7 @@ private void CheckPropertyMaps(ICollection<TypeMap> typeMapsChecked, TypeMap typ
143142
{
144143
foreach (var memberMap in typeMap.MemberMaps)
145144
{
146-
if(memberMap.Ignored || memberMap.ValueConverterConfig != null || memberMap.ValueResolverConfig != null)
145+
if(memberMap.Ignored)
147146
{
148147
continue;
149148
}

src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using AutoMapper.Internal;
1+
using AutoMapper.Execution;
2+
using AutoMapper.Internal;
23
using System;
34
using System.Collections.Generic;
45
using System.ComponentModel;
@@ -51,12 +52,12 @@ public CtorParamConfigurationExpression(string ctorParamName, Type sourceType)
5152
}
5253

5354
public void MapFrom<TMember>(Expression<Func<TSource, TMember>> sourceMember) =>
54-
_ctorParamActions.Add(cpm => cpm.CustomMapExpression = sourceMember);
55+
_ctorParamActions.Add(cpm => cpm.SetResolver(sourceMember));
5556

5657
public void MapFrom<TMember>(Func<TSource, ResolutionContext, TMember> resolver)
5758
{
5859
Expression<Func<TSource, TDestination, TMember, ResolutionContext, TMember>> resolverExpression = (src, dest, destMember, ctxt) => resolver(src, ctxt);
59-
_ctorParamActions.Add(cpm => cpm.CustomMapFunction = resolverExpression);
60+
_ctorParamActions.Add(cpm => cpm.Resolver = new FuncResolver(resolverExpression));
6061
}
6162

6263
public void MapFrom(string sourceMembersPath)
@@ -75,13 +76,12 @@ public void Configure(TypeMap typeMap)
7576
var parameter = ctorMap[CtorParamName];
7677
if (parameter == null)
7778
{
78-
throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}");
79+
throw new AutoMapperConfigurationException($"{typeMap.DestinationType.Name} does not have a matching constructor with a parameter named '{CtorParamName}'.\n{typeMap.DestinationType.FullName}");
7980
}
8081
foreach (var action in _ctorParamActions)
8182
{
8283
action(parameter);
8384
}
84-
parameter.CanResolveValue = true;
8585
}
8686
}
8787
}

0 commit comments

Comments
 (0)