Skip to content

Commit ddfd24d

Browse files
authored
Map to root target support (#65)
* Support for configuring complex type members as sources for root target mapping, re: issue #64 / Updating type converters to structs * Adding ToTarget() configuration API method * Catching invalid .ToTarget() API configurations * Start of handling runtime-type-checking of configured root target source members * Support for overwrite mapping from configured root target sources * Short-circuiting Relative member chain creation when possible * Increased test coverage for root target data sources - invalid configurations and values with no member matches * Support for conditional configured root source members * Tidying / Start of support for configured enumerable root data sources * Replacing uses of Linq Select() and Where() * Support for configured enumerable root source members * Support for multiple configured root enumerable data sources * Test coverage for configuring multiple complex type root source members and root source members inline * Support for runtime-typed configured root source members * Support for mapping configured root data sources to dictionaries * Test coverage for configured root source member mapping to dynamic
1 parent 643b307 commit ddfd24d

File tree

98 files changed

+1444
-626
lines changed

Some content is hidden

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

98 files changed

+1444
-626
lines changed

AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInline.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Linq.Expressions;
5+
using AgileMapper.Extensions;
56
using AgileMapper.Members;
67
using TestClasses;
78
using Xunit;
@@ -327,6 +328,60 @@ public void ShouldHandleANullSourceMember()
327328
}
328329
}
329330

331+
// See https://github.com/agileobjects/AgileMapper/issues/64
332+
[Fact]
333+
public void ShouldApplyAConfiguredRootSourceMember()
334+
{
335+
using (var mapper = Mapper.CreateNew())
336+
{
337+
var source = new { Value1 = 8392, Value = new { Value2 = 5482 } };
338+
339+
var result = source
340+
.MapUsing(mapper)
341+
.ToANew<PublicTwoFields<int, int>>(cfg => cfg
342+
.Map((s, ptf) => s.Value)
343+
.ToRootTarget());
344+
345+
result.Value1.ShouldBe(8392);
346+
result.Value2.ShouldBe(5482);
347+
}
348+
}
349+
350+
[Fact]
351+
public void ShouldApplyAConfiguredRootSourceObjectMember()
352+
{
353+
using (var mapper = Mapper.CreateNew())
354+
{
355+
var source1 = new PublicProperty<object>
356+
{
357+
Value = new PublicField<string> { Value = "Hello!" }
358+
};
359+
360+
var result1 = mapper
361+
.Map(source1)
362+
.ToANew<PublicProperty<string>>(cfg => cfg
363+
.Map((s, t) => s.Value)
364+
.ToRootTarget());
365+
366+
result1.Value.ShouldBe("Hello!");
367+
368+
var source2 = new PublicProperty<object>
369+
{
370+
Value = new PublicProperty<string> { Value = "Goodbye!" }
371+
};
372+
373+
var result2 = mapper
374+
.Map(source2)
375+
.ToANew<PublicProperty<string>>(cfg => cfg
376+
.Map((s, t) => s.Value)
377+
.ToRootTarget());
378+
379+
result2.Value.ShouldBe("Goodbye!");
380+
381+
mapper.InlineContexts().ShouldHaveSingleItem();
382+
}
383+
}
384+
330385
#region Helper Members
331386

332387
private static Expression<Func<IMappingData<PublicProperty<int>, PublicField<int>>, object>> SubtractOne =>

AgileMapper.UnitTests/Configuration/WhenConfiguringDataSources.cs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Collections.ObjectModel;
77
using System.Linq;
8+
using AgileMapper.Extensions;
89
using AgileMapper.Members;
910
using TestClasses;
1011
using Xunit;
@@ -993,6 +994,224 @@ public void ShouldAllowIdAndIdentifierConfiguration()
993994
}
994995
}
995996

997+
// See https://github.com/agileobjects/AgileMapper/issues/64
998+
[Fact]
999+
public void ShouldApplyAConfiguredRootSourceMember()
1000+
{
1001+
using (var mapper = Mapper.CreateNew())
1002+
{
1003+
var source = new { Value1 = 123, Value = new { Value2 = 456 } };
1004+
1005+
mapper.WhenMapping
1006+
.From(source)
1007+
.To<PublicTwoFields<int, int>>()
1008+
.Map((s, ptf) => s.Value)
1009+
.ToRootTarget();
1010+
1011+
var result = source
1012+
.MapUsing(mapper)
1013+
.ToANew<PublicTwoFields<int, int>>();
1014+
1015+
result.Value1.ShouldBe(123);
1016+
result.Value2.ShouldBe(456);
1017+
}
1018+
}
1019+
1020+
[Fact]
1021+
public void ShouldApplyANestedOverwriteConfiguredRootSourceMember()
1022+
{
1023+
using (var mapper = Mapper.CreateNew())
1024+
{
1025+
mapper.WhenMapping
1026+
.From<PublicTwoFields<int, PublicField<PublicTwoFields<int, int>>>>()
1027+
.Over<PublicTwoFields<int, int>>()
1028+
.Map((s, t) => s.Value2.Value)
1029+
.ToRootTarget();
1030+
1031+
var source = new PublicTwoFields<int, PublicField<PublicTwoFields<int, int>>>
1032+
{
1033+
Value1 = 6372,
1034+
Value2 = new PublicField<PublicTwoFields<int, int>>
1035+
{
1036+
Value = new PublicTwoFields<int, int>
1037+
{
1038+
Value2 = 8262
1039+
}
1040+
}
1041+
};
1042+
1043+
var target = new PublicTwoFields<int, int>
1044+
{
1045+
Value1 = 637,
1046+
Value2 = 728
1047+
};
1048+
1049+
mapper.Map(source).Over(target);
1050+
1051+
target.Value1.ShouldBeDefault(); // <- Because Value2.Value.Value1 will overwrite 6372
1052+
target.Value2.ShouldBe(8262);
1053+
}
1054+
}
1055+
1056+
[Fact]
1057+
public void ShouldHandleAConfiguredRootSourceMemberNullValue()
1058+
{
1059+
using (var mapper = Mapper.CreateNew())
1060+
{
1061+
mapper.WhenMapping
1062+
.From<MysteryCustomer>()
1063+
.ToANew<PublicTwoFields<string, string>>()
1064+
.Map((mc, t) => mc.Name)
1065+
.To(t => t.Value1)
1066+
.And
1067+
.Map((mc, t) => mc.Address)
1068+
.ToRootTarget();
1069+
1070+
var source = new MysteryCustomer { Name = "Nelly", Address = default(Address) };
1071+
1072+
var result = mapper.Map(source).ToANew<PublicTwoFields<string, string>>();
1073+
1074+
result.Value1.ShouldBe("Nelly");
1075+
result.Value2.ShouldBeNull();
1076+
}
1077+
}
1078+
1079+
[Fact]
1080+
public void ShouldApplyAConfiguredRootSourceMemberConditionally()
1081+
{
1082+
using (var mapper = Mapper.CreateNew())
1083+
{
1084+
mapper.WhenMapping
1085+
.From<PublicTwoFieldsStruct<PublicPropertyStruct<int>, int>>()
1086+
.OnTo<PublicTwoFields<int, int>>()
1087+
.If((s, t) => s.Value1.Value > 5)
1088+
.Map((s, t) => s.Value1)
1089+
.ToRootTarget();
1090+
1091+
mapper.WhenMapping
1092+
.From<PublicPropertyStruct<int>>()
1093+
.OnTo<PublicTwoFields<int, int>>()
1094+
.Map((s, t) => s.Value)
1095+
.To(t => t.Value1);
1096+
1097+
var matchingSource = new PublicTwoFieldsStruct<PublicPropertyStruct<int>, int>
1098+
{
1099+
Value1 = new PublicPropertyStruct<int> { Value = 10 },
1100+
Value2 = 627
1101+
};
1102+
1103+
var target = new PublicTwoFields<int, int> { Value2 = 673282 };
1104+
1105+
mapper.Map(matchingSource).OnTo(target);
1106+
1107+
target.Value1.ShouldBe(10);
1108+
target.Value2.ShouldBe(673282);
1109+
1110+
var nonMatchingSource = new PublicTwoFieldsStruct<PublicPropertyStruct<int>, int>
1111+
{
1112+
Value1 = new PublicPropertyStruct<int> { Value = 1 },
1113+
Value2 = 9285
1114+
};
1115+
1116+
target.Value1 = target.Value2 = default(int);
1117+
1118+
mapper.Map(nonMatchingSource).OnTo(target);
1119+
1120+
target.Value1.ShouldBeDefault();
1121+
target.Value2.ShouldBe(9285);
1122+
}
1123+
}
1124+
1125+
[Fact]
1126+
public void ShouldApplyAConfiguredRootSourceEnumerableMember()
1127+
{
1128+
using (var mapper = Mapper.CreateNew())
1129+
{
1130+
mapper.WhenMapping
1131+
.From<PublicTwoFields<Address, Address[]>>()
1132+
.To<List<Address>>()
1133+
.Map((s, r) => s.Value2)
1134+
.ToRootTarget();
1135+
1136+
var source = new PublicTwoFields<Address, Address[]>
1137+
{
1138+
Value1 = new Address { Line1 = "Here", Line2 = "There" },
1139+
Value2 = new[]
1140+
{
1141+
new Address { Line1 = "Somewhere", Line2 = "Else" },
1142+
new Address { Line1 = "Elsewhere"}
1143+
}
1144+
};
1145+
1146+
var result = mapper.Map(source).ToANew<List<Address>>();
1147+
1148+
result.Count.ShouldBe(2);
1149+
result.First().Line1.ShouldBe("Somewhere");
1150+
result.First().Line2.ShouldBe("Else");
1151+
result.Second().Line1.ShouldBe("Elsewhere");
1152+
result.Second().Line2.ShouldBeNull();
1153+
1154+
source.Value2 = null;
1155+
1156+
var nullResult = mapper.Map(source).ToANew<List<Address>>();
1157+
1158+
nullResult.ShouldBeEmpty();
1159+
}
1160+
}
1161+
1162+
[Fact]
1163+
public void ShouldApplyMultipleConfiguredRootSourceComplexTypeMembers()
1164+
{
1165+
using (var mapper = Mapper.CreateNew())
1166+
{
1167+
var source = new
1168+
{
1169+
PropertyOne = new { Value1 = "Value 1!" },
1170+
PropertyTwo = new { Value2 = "Value 2!" },
1171+
};
1172+
1173+
mapper.WhenMapping
1174+
.From(source)
1175+
.To<PublicTwoFields<string, string>>()
1176+
.Map((s, t) => s.PropertyOne)
1177+
.ToRootTarget()
1178+
.And
1179+
.Map((s, t) => s.PropertyTwo)
1180+
.ToRootTarget();
1181+
1182+
var result = mapper.Map(source).ToANew<PublicTwoFields<string, string>>();
1183+
1184+
result.Value1.ShouldBe("Value 1!");
1185+
result.Value2.ShouldBe("Value 2!");
1186+
}
1187+
}
1188+
1189+
[Fact]
1190+
public void ShouldApplyMultipleConfiguredRootSourceEnumerableMembers()
1191+
{
1192+
using (var mapper = Mapper.CreateNew())
1193+
{
1194+
mapper.WhenMapping
1195+
.From<PublicTwoFields<int[], long[]>>()
1196+
.To<decimal[]>()
1197+
.Map((s, t) => s.Value1)
1198+
.ToRootTarget()
1199+
.And
1200+
.Map((s, t) => s.Value2)
1201+
.ToRootTarget();
1202+
1203+
var source = new PublicTwoFields<int[], long[]>
1204+
{
1205+
Value1 = new[] { 1, 2, 3 },
1206+
Value2 = new[] { 1L, 2L, 3L }
1207+
};
1208+
1209+
var result = mapper.Map(source).ToANew<decimal[]>();
1210+
1211+
result.Length.ShouldBe(6);
1212+
}
1213+
}
1214+
9961215
// ReSharper disable once ClassNeverInstantiated.Local
9971216
// ReSharper disable UnusedAutoPropertyAccessor.Local
9981217
private class IdTester

0 commit comments

Comments
 (0)