Skip to content

Commit 555db84

Browse files
authored
Merge pull request #3405 from lbargaoanu/cycles
Improve cycle detection with inheritance
2 parents 91e4fa3 + 53e760a commit 555db84

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

src/AutoMapper/Execution/TypeMapPlanBuilder.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ private void CheckForCycles(HashSet<TypeMap> typeMapsPath)
7979
}
8080
typeMapsPath.Add(_typeMap);
8181
var members =
82-
_typeMap.MemberMaps.Where(pm=>pm.CanResolveValue)
82+
_typeMap.MemberMaps
83+
.Concat(_typeMap.IncludedDerivedTypes.Select(ResolveTypeMap).SelectMany(tm=>tm.MemberMaps))
84+
.Where(pm=>pm.CanResolveValue)
8385
.ToArray()
8486
.Select(pm=> new { MemberTypeMap = ResolveMemberTypeMap(pm), MemberMap = pm })
8587
.Where(p => p.MemberTypeMap != null && !p.MemberTypeMap.PreserveReferences && p.MemberTypeMap.MapExpression == null);
@@ -103,7 +105,6 @@ private void CheckForCycles(HashSet<TypeMap> typeMapsPath)
103105
typeMapsPath.Remove(_typeMap);
104106
return;
105107
}
106-
107108
SetPreserveReferences(memberTypeMap);
108109
foreach(var derivedTypeMap in memberTypeMap.IncludedDerivedTypes.Select(ResolveTypeMap))
109110
{
@@ -123,7 +124,7 @@ void SetPreserveReferences(TypeMap memberTypeMap)
123124

124125
TypeMap ResolveMemberTypeMap(IMemberMap memberMap)
125126
{
126-
if(memberMap.SourceType == null)
127+
if(memberMap.SourceType == null || memberMap.Types.ContainsGenericParameters)
127128
{
128129
return null;
129130
}

src/AutoMapper/TypePair.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ public static TypePair Create<TSource, TDestination>(TSource source, TDestinatio
9292

9393
public bool IsGenericTypeDefinition => SourceType.IsGenericTypeDefinition || DestinationType.IsGenericTypeDefinition;
9494

95+
public bool ContainsGenericParameters => SourceType.ContainsGenericParameters || DestinationType.ContainsGenericParameters;
96+
9597
public TypePair? GetOpenGenericTypePair()
9698
{
9799
if(!IsGeneric)

src/UnitTests/BidirectionalRelationshipsWithoutPR.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,70 @@
11
using System;
2+
using System.CodeDom;
23
using System.Collections.Generic;
34
using System.Linq;
45
using Shouldly;
56
using Xunit;
67

78
namespace AutoMapper.UnitTests
89
{
10+
public class CyclesWithInheritance : AutoMapperSpecBase
11+
{
12+
class FlowChart
13+
{
14+
public FlowNode[] Nodes;
15+
}
16+
class FlowNode
17+
{
18+
}
19+
class FlowStep : FlowNode
20+
{
21+
public FlowNode Next;
22+
}
23+
class FlowDecision : FlowNode
24+
{
25+
public FlowNode True;
26+
public FlowNode False;
27+
}
28+
class FlowSwitch<T> : FlowNode
29+
{
30+
public IDictionary<T, object> Connections;
31+
}
32+
class FlowChartModel
33+
{
34+
public FlowNodeModel[] Nodes;
35+
}
36+
class FlowNodeModel
37+
{
38+
public Connection[] Connections;
39+
}
40+
class Connection
41+
{
42+
public FlowNodeModel Node;
43+
}
44+
protected override MapperConfiguration Configuration => new MapperConfiguration(cfg=>
45+
{
46+
cfg.CreateMap<FlowChart, FlowChartModel>();
47+
cfg.CreateMap<FlowNode, FlowNodeModel>()
48+
.Include<FlowStep, FlowNodeModel>()
49+
.Include<FlowDecision, FlowNodeModel>()
50+
.Include(typeof(FlowSwitch<>), typeof(FlowNodeModel))
51+
.ForMember(d=>d.Connections, o=>o.Ignore());
52+
cfg.CreateMap<FlowStep, FlowNodeModel>().ForMember(d => d.Connections, o => o.MapFrom(s => new[] { s.Next }));
53+
cfg.CreateMap<FlowDecision, FlowNodeModel>().ForMember(d => d.Connections, o => o.MapFrom(s => new[] { s.True, s.False }));
54+
cfg.CreateMap(typeof(FlowSwitch<>), typeof(FlowNodeModel));
55+
cfg.CreateMap<FlowNode, Connection>().ForMember(d => d.Node, o => o.MapFrom(s => s));
56+
cfg.CreateMap(typeof(KeyValuePair<,>), typeof(Connection)).ForMember("Node", o => o.MapFrom("Key"));
57+
});
58+
[Fact]
59+
public void Should_map_ok()
60+
{
61+
var flowStep = new FlowStep();
62+
var flowDecision = new FlowDecision { False = flowStep, True = flowStep };
63+
flowStep.Next = flowDecision;
64+
var source = new FlowChart { Nodes = new FlowNode[] { flowStep, flowDecision } };
65+
var dest = Map<FlowChartModel>(source);
66+
}
67+
}
968
public class When_the_source_has_cyclical_references_with_dynamic_map : AutoMapperSpecBase
1069
{
1170
public class CDataTypeModel<T>

0 commit comments

Comments
 (0)